Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user-install microG User Services #2188

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,137 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.IBinder;
import android.util.Log;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
import static org.microg.gms.common.Constants.USER_MICROG_PACKAGE_NAME;
import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1;
import static org.microg.gms.common.Constants.GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1;
import static org.microg.gms.common.Constants.MICROG_PACKAGE_SIGNATURE_SHA1;

public class MultiConnectionKeeper {
private static final String TAG = "GmsMultiConKeeper";
private static final String PREF_NAME = "org.microg.gms_connection";
private static final String PREF_TARGET = "target";
private static final String[] GOOGLE_PRIMARY_KEYS = {GMS_PACKAGE_SIGNATURE_SHA1, GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1};

private static final String[] MICROG_PRIMARY_KEYS = {MICROG_PACKAGE_SIGNATURE_SHA1};
private static MultiConnectionKeeper INSTANCE;

private final Context context;

private final String targetPackage;
private final Map<String, Connection> connections = new HashMap<String, Connection>();

private Boolean isSystem(PackageManager pm, String packageId) throws PackageManager.NameNotFoundException {
ApplicationInfo ai = pm.getApplicationInfo(packageId, PackageManager.GET_META_DATA);
return (ai.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
}

private Boolean isGoogleOrMicrogSig(PackageManager pm, String packageId) throws PackageManager.NameNotFoundException {
List<String> signatures = new LinkedList<>(Arrays.asList(GOOGLE_PRIMARY_KEYS));
signatures.addAll(Arrays.asList(MICROG_PRIMARY_KEYS));
return signatureIsIn(pm, packageId, signatures);
}

private Boolean isSystemGoogleOrMicrogSig(PackageManager pm, String packageId) throws PackageManager.NameNotFoundException {
return isSystem(pm, packageId) || isGoogleOrMicrogSig(pm, packageId);
}

private Boolean isMicrogSig(PackageManager pm, String packageId) throws PackageManager.NameNotFoundException {
List<String> signatures = Arrays.asList(MICROG_PRIMARY_KEYS);
return signatureIsIn(pm, packageId, signatures);
}

private Boolean signatureIsIn(PackageManager pm, String packageId, List<String> signatures) throws PackageManager.NameNotFoundException {
Signature[] appSignatures = pm.getPackageInfo(packageId, PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : appSignatures) {
if (sig != null && signatures.contains(sha1sum(sig.toByteArray())))
return true;
}
return false;
}

private String sha1sum(byte[] bytes) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA1");
} catch (final NoSuchAlgorithmException e) {
return null;
}
if (md != null) {
bytes = md.digest(bytes);
if (bytes != null) {
StringBuilder sb = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
return null;
}

private String getTargetPackageWithoutPref() {
// Pref: gms > microG > self
PackageManager pm = context.getPackageManager();
try {
if (isSystemGoogleOrMicrogSig(pm, GMS_PACKAGE_NAME)) {
Log.d(TAG, GMS_PACKAGE_NAME + " found !");
return GMS_PACKAGE_NAME;
} else {
Log.w(TAG, GMS_PACKAGE_NAME + " found with another signature");
}
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, GMS_PACKAGE_NAME + " not found");
}
try {
if (isMicrogSig(pm, USER_MICROG_PACKAGE_NAME)) {
Log.d(TAG, USER_MICROG_PACKAGE_NAME + " found !");
return USER_MICROG_PACKAGE_NAME;
} else {
Log.w(TAG, USER_MICROG_PACKAGE_NAME + " found with another signature");
}
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, USER_MICROG_PACKAGE_NAME + " not found");
}
return context.getPackageName();
}

private String getTargetPackage() {
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
String target;
if ((target = prefs.getString(PREF_TARGET, null)) != null) {
return target;
}
target = getTargetPackageWithoutPref();
prefs.edit().putString(PREF_TARGET, target).apply();
return target;
}

public MultiConnectionKeeper(Context context) {
this.context = context;
targetPackage = getTargetPackage();
}

public synchronized static MultiConnectionKeeper getInstance(Context context) {
Expand Down Expand Up @@ -138,32 +243,46 @@ public Connection(String actionString, boolean requireMicrog) {
this.requireMicrog = requireMicrog;
}

private Intent getIntent() {
Intent intent;
ResolveInfo resolveInfo;
PackageManager pm = context.getPackageManager();
if (!Objects.equals(targetPackage, context.getPackageName())) {
intent = new Intent(actionString).setPackage(targetPackage);
try {
if ((resolveInfo = context.getPackageManager().resolveService(intent, 0)) != null) {
if (requireMicrog && !isMicrog(resolveInfo)) {
Log.w(TAG, "GMS service found for " + actionString + " but looks not like microG");
} else {
if (isSystemGoogleOrMicrogSig(pm, targetPackage)){
Log.d(TAG, "GMS service found for " + actionString);
return intent;
} else {
Log.w(TAG, "GMS service found for " + actionString + " but is not system, and doesn't have microG or Google signature");
}
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, targetPackage + " not found");
}
}
intent = new Intent(actionString).setPackage(context.getPackageName());
if (context.getPackageManager().resolveService(intent, 0) != null) {
Log.d(TAG, "Found service for " + actionString + " in self package, using it instead");
return intent;
}
return null;
}

@SuppressLint("InlinedApi")
public void bind() {
Log.d(TAG, "Connection(" + actionString + ") : bind()");
Intent gmsIntent = new Intent(actionString).setPackage(GMS_PACKAGE_NAME);
Intent selfIntent = new Intent(actionString).setPackage(context.getPackageName());
Intent intent;
ResolveInfo resolveInfo;
if ((resolveInfo = context.getPackageManager().resolveService(gmsIntent, 0)) == null) {
Log.w(TAG, "No GMS service found for " + actionString);
if (context.getPackageManager().resolveService(selfIntent, 0) != null) {
Log.d(TAG, "Found service for " + actionString + " in self package, using it instead");
intent = selfIntent;
} else {
return;
}
} else if (requireMicrog && !isMicrog(resolveInfo)) {
Log.w(TAG, "GMS service found for " + actionString + " but looks not like microG");
if (context.getPackageManager().resolveService(selfIntent, 0) != null) {
Log.d(TAG, "Found service for " + actionString + " in self package, using it instead");
intent = selfIntent;
} else {
intent = gmsIntent;
}
} else {
intent = gmsIntent;
if ((intent = getIntent()) == null) {
Log.w(TAG, "No service found for " + actionString);
return;
}

int flags = Context.BIND_AUTO_CREATE | Context.BIND_DEBUG_UNBIND;
if (SDK_INT >= ICE_CREAM_SANDWICH) {
flags |= Context.BIND_ADJUST_WITH_ACTIVITY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
public class Constants {
public static final int GMS_VERSION_CODE = (BuildConfig.VERSION_CODE / 1000) * 1000;
public static final String GMS_PACKAGE_NAME = "com.google.android.gms";
public static final String USER_MICROG_PACKAGE_NAME = "org.microg.gms";
public static final String GSF_PACKAGE_NAME = "com.google.android.gsf";
public static final String GMS_PACKAGE_SIGNATURE_SHA1 = "38918a453d07199354f8b19af05ec6562ced5788";
public static final String GMS_SECONDARY_PACKAGE_SIGNATURE_SHA1 = "bd32424203e0fb25f36b57e5aa356f9bdd1da998";
public static final String MICROG_PACKAGE_SIGNATURE_SHA1 = "10321bd893f69af97f7573aafe9de1dc0901f3a1";
@Deprecated
public static final int MAX_REFERENCE_VERSION = GMS_VERSION_CODE;
}
11 changes: 11 additions & 0 deletions play-services-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ android {
multiDexEnabled true
multiDexKeepProguard file('multidex-keep.pro')

manifestPlaceholders = [appLabel:"@string/gms_app_name"]
resValue "string", "package_id", "com.google.android.gms"

buildConfigField "String", "SAFETYNET_KEY", "\"${localProperties.get("safetynet.key", "")}\""
buildConfigField "String", "RECAPTCHA_SITE_KEY", "\"${localProperties.get("recaptcha.siteKey", "")}\""
buildConfigField "String", "RECAPTCHA_SECRET", "\"${localProperties.get("recaptcha.secret", "")}\""
Expand Down Expand Up @@ -160,6 +163,14 @@ android {
versionCode appVersionCode - 1000
matchingFallbacks = ['huawei']
}
"user" {
dimension 'target'
applicationId = "org.microg.gms"
versionNameSuffix "-user"
manifestPlaceholders = [appLabel:"@string/limited_services_app_name"]
matchingFallbacks = ['default']
resValue "string", "package_id", "org.microg.gms"
}
"hms" {
dimension 'maps'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.microg.tools.ui;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -53,11 +54,11 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
return scrollRoot;
}

protected abstract void prepareSelfCheckList(List<SelfCheckGroup> checks);
protected abstract void prepareSelfCheckList(Context context, List<SelfCheckGroup> checks);

protected void reset(LayoutInflater inflater) {
List<SelfCheckGroup> selfCheckGroupList = new ArrayList<SelfCheckGroup>();
prepareSelfCheckList(selfCheckGroupList);
prepareSelfCheckList(getContext(), selfCheckGroupList);

root.removeAllViews();
for (SelfCheckGroup group : selfCheckGroupList) {
Expand Down
2 changes: 1 addition & 1 deletion play-services-core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
android:fullBackupOnly="true"
android:icon="@mipmap/ic_app"
android:roundIcon="@mipmap/ic_app"
android:label="@string/gms_app_name"
android:label="${appLabel}"
android:multiArch="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/Theme.App">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.microg.gms.ui;

import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;

import androidx.annotation.Nullable;
Expand All @@ -11,19 +12,41 @@
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.google.android.gms.BuildConfig;
import com.google.android.gms.R;
import com.google.android.material.appbar.CollapsingToolbarLayout;

import org.microg.gms.common.Constants;
import org.microg.gms.ui.settings.SettingsProvider;

import static org.microg.gms.ui.UtilsKt.buildAlertDialog;
import static org.microg.gms.ui.settings.SettingsProviderKt.getAllSettingsProviders;

public class MainSettingsActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;

private static final String FIRST_RUN_MASTER = "org.microg.gms_firstRun";
private static final String FIRST_RUN_PREF = "as_run";

private NavController getNavController() {
return ((NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.navhost)).getNavController();
}

private void showDialogIfNeeded() {
SharedPreferences prefs = getSharedPreferences(FIRST_RUN_MASTER, MODE_PRIVATE);
if (BuildConfig.APPLICATION_ID == Constants.USER_MICROG_PACKAGE_NAME &&
prefs.getBoolean(FIRST_RUN_PREF, true)) {
buildAlertDialog(this)
.setMessage(R.string.limited_services_dialog_information)
.setTitle(R.string.limited_services_app_name)
.setPositiveButton(R.string.limited_services_dialog_information_ack, (dialog, id) -> {
prefs.edit().putBoolean(FIRST_RUN_PREF, false).apply();
})
.create()
.show();
}
}

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -45,6 +68,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {

appBarConfiguration = new AppBarConfiguration.Builder(getNavController().getGraph()).build();
NavigationUI.setupWithNavController(toolbarLayout, toolbar, getNavController(), appBarConfiguration);
showDialogIfNeeded();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import org.microg.gms.common.Constants;
import org.microg.tools.selfcheck.InstalledPackagesChecks;
//import org.microg.tools.selfcheck.NlpOsCompatChecks;
//import org.microg.tools.selfcheck.NlpStatusChecks;
Expand All @@ -41,6 +42,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
Expand All @@ -57,8 +59,10 @@
public class SelfCheckFragment extends AbstractSelfCheckFragment {

@Override
protected void prepareSelfCheckList(List<SelfCheckGroup> checks) {
checks.add(new RomSpoofSignatureChecks());
protected void prepareSelfCheckList(Context context, List<SelfCheckGroup> checks) {
if (Objects.equals(context.getPackageName(), Constants.GMS_PACKAGE_NAME)) {
checks.add(new RomSpoofSignatureChecks());
}
checks.add(new InstalledPackagesChecks());
if (SDK_INT >= 23) {
List<String> permissions = new ArrayList<>();
Expand Down
4 changes: 4 additions & 0 deletions play-services-core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="gms_app_name">microG Services</string>
<string name="limited_services_app_name">microG Limited Services</string>
<string name="gms_settings_name">microG Settings</string>
<string name="gms_settings_summary">Setup microG services.</string>

Expand Down Expand Up @@ -279,4 +280,7 @@ This can take a couple of minutes."</string>
<string name="credentials_assisted_choose_account_subtitle">to continue to %1$s</string>
<string name="credentials_assisted_signin_button_text_long">Sign in with Google</string>

<string name="limited_services_dialog_information">You are using the microG Limited Services. Unlike the usual microG Services, this flavor only works with apps using microG libraries, not those on Google Play. This means that most applications will ignore these services.</string>
<string name="limited_services_dialog_information_ack">I understand</string>

</resources>
Loading