From ff47e4fe55fd81d76cc3ec7660118da1524e7b2d Mon Sep 17 00:00:00 2001 From: "Mr.Dragon" Date: Thu, 13 Sep 2018 02:32:40 +0530 Subject: [PATCH] Add Support for Aurora Services as Installation Client --- .../aurora/services/IPrivilegedCallback.aidl | 23 ++++ .../aurora/services/IPrivilegedService.aidl | 63 +++++++++++ .../main/java/com/dragons/aurora/Aurora.java | 1 + .../com/dragons/aurora/InstallerAurora.java | 106 ++++++++++++++++++ .../com/dragons/aurora/InstallerFactory.java | 2 + .../OnInstallationMethodChangeListener.java | 15 ++- .../aurora/task/ConvertToSystemTask.java | 6 +- .../aurora/task/SystemRemountTask.java | 19 +--- app/src/main/res/values/array.xml | 2 + app/src/main/res/values/strings.xml | 2 + 10 files changed, 218 insertions(+), 21 deletions(-) create mode 100644 app/src/main/aidl/com/aurora/services/IPrivilegedCallback.aidl create mode 100644 app/src/main/aidl/com/aurora/services/IPrivilegedService.aidl create mode 100644 app/src/main/java/com/dragons/aurora/InstallerAurora.java diff --git a/app/src/main/aidl/com/aurora/services/IPrivilegedCallback.aidl b/app/src/main/aidl/com/aurora/services/IPrivilegedCallback.aidl new file mode 100644 index 000000000..5f0e8dfb4 --- /dev/null +++ b/app/src/main/aidl/com/aurora/services/IPrivilegedCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015-2016 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.aurora.services; + +interface IPrivilegedCallback { + + void handleResult(in String packageName, in int returnCode); + +} \ No newline at end of file diff --git a/app/src/main/aidl/com/aurora/services/IPrivilegedService.aidl b/app/src/main/aidl/com/aurora/services/IPrivilegedService.aidl new file mode 100644 index 000000000..5c9fe7273 --- /dev/null +++ b/app/src/main/aidl/com/aurora/services/IPrivilegedService.aidl @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015-2016 Dominik Schürmann + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.aurora.services; + +import com.aurora.services.IPrivilegedCallback; + +interface IPrivilegedService { + + boolean hasPrivilegedPermissions(); + + /** + * - Docs based on PackageManager.installPackage() + * - Asynchronous (oneway) IPC calls! + * + * Install a package. Since this may take a little while, the result will + * be posted back to the given callback. An installation will fail if the + * package named in the package file's manifest is already installed, or if there's no space + * available on the device. + * + * @param packageURI The location of the package file to install. This can be a 'file:' or a + * 'content:' URI. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that is performing the + * installation. This identifies which market the package came from. + * @param callback An callback to get notified when the package installation is + * complete. + */ + oneway void installPackage(in Uri packageURI, in int flags, in String installerPackageName, + in IPrivilegedCallback callback); + + + /** + * - Docs based on PackageManager.deletePackage() + * - Asynchronous (oneway) IPC calls! + * + * Attempts to delete a package. Since this may take a little while, the result will + * be posted back to the given observer. A deletion will fail if the + * named package cannot be found, or if the named package is a "system package". + * + * @param packageName The name of the package to delete + * @param flags - possible values: {@link #DELETE_KEEP_DATA}, + * {@link #DELETE_ALL_USERS}. + * @param callback An callback to get notified when the package deletion is + * complete. + */ + oneway void deletePackage(in String packageName, in int flags, in IPrivilegedCallback callback); + +} \ No newline at end of file diff --git a/app/src/main/java/com/dragons/aurora/Aurora.java b/app/src/main/java/com/dragons/aurora/Aurora.java index 01cb6c5d1..1e7f18bb0 100644 --- a/app/src/main/java/com/dragons/aurora/Aurora.java +++ b/app/src/main/java/com/dragons/aurora/Aurora.java @@ -35,6 +35,7 @@ public class Aurora { public static final String PREFERENCE_SWIPE_PAGES = "PREFERENCE_SWIPE_PAGES"; public static final String PREFERENCE_COLOR_NAV = "PREFERENCE_COLOR_NAV"; public static final String PREFERENCE_SHOW_IME = "PREFERENCE_SHOW_IME"; + public static final String INSTALLATION_METHOD_AURORA = "AURORA"; public static final String INSTALLATION_METHOD_ROOT = "ROOT"; public static final String INSTALLATION_METHOD_PRIVILEGED = "PRIVILEGED"; public static final String INSTALLATION_METHOD_DEFAULT = "DEFAULT"; diff --git a/app/src/main/java/com/dragons/aurora/InstallerAurora.java b/app/src/main/java/com/dragons/aurora/InstallerAurora.java new file mode 100644 index 000000000..254615d67 --- /dev/null +++ b/app/src/main/java/com/dragons/aurora/InstallerAurora.java @@ -0,0 +1,106 @@ +package com.dragons.aurora; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.aurora.services.IPrivilegedCallback; +import com.aurora.services.IPrivilegedService; +import com.dragons.aurora.model.App; + +public class InstallerAurora extends InstallerAbstract { + + public static final String PRIVILEGED_EXTENSION_PACKAGE_NAME = "com.aurora.services"; + public static final int ACTION_INSTALL_REPLACE_EXISTING = 2; + private static final String PRIVILEGED_EXTENSION_SERVICE_INTENT = "com.aurora.services.IPrivilegedService"; + + public InstallerAurora(Context context) { + super(context); + } + + private static boolean isExtensionInstalled(Context context) { + PackageManager pm = context.getPackageManager(); + try { + pm.getPackageInfo(PRIVILEGED_EXTENSION_PACKAGE_NAME, PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + public static boolean isExtensionAvailable(Context context) { + if (!isExtensionInstalled(context)) { + return false; + } + ServiceConnection serviceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + Intent serviceIntent = new Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT); + serviceIntent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME); + + try { + context.getApplicationContext().bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE); + return true; + } catch (SecurityException e) { + return false; + } + } + + @Override + public boolean verify(App app) { + if (!super.verify(app)) { + return false; + } + return isExtensionAvailable(context); + } + + @Override + protected void install(final App app) { + InstallationState.setInstalling(app.getPackageName()); + ServiceConnection mServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder binder) { + IPrivilegedService service = IPrivilegedService.Stub.asInterface(binder); + IPrivilegedCallback callback = new IPrivilegedCallback.Stub() { + @Override + public void handleResult(String packageName, int returnCode) throws RemoteException { + Log.i(getClass().getSimpleName(), "Installation of " + packageName + " complete with code " + returnCode); + sendBroadcast(packageName, returnCode > 0); + } + }; + try { + if (!service.hasPrivilegedPermissions()) { + Log.e(getClass().getSimpleName(), "service.hasPrivilegedPermissions() is false"); + sendBroadcast(app.getPackageName(), false); + return; + } + service.installPackage( + Uri.fromFile(Paths.getApkPath(context, app.getPackageName(), app.getVersionCode())), + ACTION_INSTALL_REPLACE_EXISTING, + BuildConfig.APPLICATION_ID, + callback + ); + } catch (RemoteException e) { + Log.e(getClass().getSimpleName(), "Connecting to privileged service failed"); + sendBroadcast(app.getPackageName(), false); + } + } + + public void onServiceDisconnected(ComponentName name) { + } + }; + + Intent serviceIntent = new Intent(PRIVILEGED_EXTENSION_SERVICE_INTENT); + serviceIntent.setPackage(PRIVILEGED_EXTENSION_PACKAGE_NAME); + context.getApplicationContext().bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } +} diff --git a/app/src/main/java/com/dragons/aurora/InstallerFactory.java b/app/src/main/java/com/dragons/aurora/InstallerFactory.java index 666e366b5..72ee370b0 100644 --- a/app/src/main/java/com/dragons/aurora/InstallerFactory.java +++ b/app/src/main/java/com/dragons/aurora/InstallerFactory.java @@ -30,6 +30,8 @@ public class InstallerFactory { static public InstallerAbstract get(Context context) { String userChoice = Prefs.getString(context, Aurora.PREFERENCE_INSTALLATION_METHOD); switch (userChoice) { + case Aurora.INSTALLATION_METHOD_AURORA: + return new InstallerAurora(context); case Aurora.INSTALLATION_METHOD_PRIVILEGED: return new InstallerPrivileged(context); case Aurora.INSTALLATION_METHOD_ROOT: diff --git a/app/src/main/java/com/dragons/aurora/fragment/preference/OnInstallationMethodChangeListener.java b/app/src/main/java/com/dragons/aurora/fragment/preference/OnInstallationMethodChangeListener.java index 2dd5f4984..b72119ef9 100644 --- a/app/src/main/java/com/dragons/aurora/fragment/preference/OnInstallationMethodChangeListener.java +++ b/app/src/main/java/com/dragons/aurora/fragment/preference/OnInstallationMethodChangeListener.java @@ -24,10 +24,11 @@ package com.dragons.aurora.fragment.preference; import android.Manifest; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.widget.Toast; import com.dragons.aurora.Aurora; import com.dragons.aurora.BuildConfig; +import com.dragons.aurora.ContextUtil; +import com.dragons.aurora.InstallerAurora; import com.dragons.aurora.R; import com.dragons.aurora.fragment.PreferenceFragment; import com.dragons.aurora.model.App; @@ -50,7 +51,12 @@ class OnInstallationMethodChangeListener implements Preference.OnPreferenceChang public boolean onPreferenceChange(Preference preference, Object newValue) { String oldValue = ((ListPreference) preference).getValue(); if (null != oldValue && !oldValue.equals(newValue)) { - if (Aurora.INSTALLATION_METHOD_PRIVILEGED.equals(newValue)) { + if (Aurora.INSTALLATION_METHOD_AURORA.equals(newValue)) { + if (!InstallerAurora.isExtensionAvailable(activity.getActivity())) { + ContextUtil.toast(activity.getActivity(), R.string.pref_installation_method_aurora_unavailable); + return false; + } + } else if (Aurora.INSTALLATION_METHOD_PRIVILEGED.equals(newValue)) { if (!checkPrivileged()) { return false; } @@ -68,6 +74,9 @@ class OnInstallationMethodChangeListener implements Preference.OnPreferenceChang } int summaryId; switch (installationMethod) { + case Aurora.INSTALLATION_METHOD_AURORA: + summaryId = R.string.pref_installation_method_aurora; + break; case Aurora.INSTALLATION_METHOD_PRIVILEGED: summaryId = R.string.pref_installation_method_privileged; break; @@ -98,7 +107,7 @@ class OnInstallationMethodChangeListener implements Preference.OnPreferenceChang @Override protected void onPostExecute(Void aVoid) { if (!available) { - Toast.makeText(fragment.getActivity().getApplicationContext(), R.string.pref_not_privileged, Toast.LENGTH_LONG).show(); + ContextUtil.toast(fragment.getActivity(), R.string.pref_not_privileged); return; } showPrivilegedInstallationDialog(); diff --git a/app/src/main/java/com/dragons/aurora/task/ConvertToSystemTask.java b/app/src/main/java/com/dragons/aurora/task/ConvertToSystemTask.java index 41323cc77..0969e37be 100644 --- a/app/src/main/java/com/dragons/aurora/task/ConvertToSystemTask.java +++ b/app/src/main/java/com/dragons/aurora/task/ConvertToSystemTask.java @@ -55,11 +55,9 @@ public class ConvertToSystemTask extends SystemRemountTask { private String getTargetPath() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return "/system/priv-app/" + app.getPackageName() + "/" + app.getPackageName() + ".apk"; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - return "/system/priv-app/" + app.getPackageName() + ".apk"; + return "/system/priv-app/" + app.getPackageName() + File.separator + app.getPackageName() + ".apk"; } else { - return "/system/app/" + app.getPackageName() + ".apk"; + return "/system/priv-app/" + app.getPackageName() + ".apk"; } } } diff --git a/app/src/main/java/com/dragons/aurora/task/SystemRemountTask.java b/app/src/main/java/com/dragons/aurora/task/SystemRemountTask.java index c2fac1d63..486c64102 100644 --- a/app/src/main/java/com/dragons/aurora/task/SystemRemountTask.java +++ b/app/src/main/java/com/dragons/aurora/task/SystemRemountTask.java @@ -94,20 +94,11 @@ public abstract class SystemRemountTask extends TaskWithProgress> { new AlertDialog.Builder(context) .setMessage(R.string.dialog_message_reboot_required) .setTitle(R.string.dialog_title_reboot_required) - .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - new RebootTask().execute(); - dialog.dismiss(); - } + .setPositiveButton(android.R.string.yes, (dialog, which) -> { + new RebootTask().execute(); + dialog.dismiss(); }) - .setNegativeButton(R.string.dialog_two_factor_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .show() - ; + .setNegativeButton(R.string.dialog_two_factor_cancel, (dialog, which) -> dialog.dismiss()) + .show(); } } diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index c5c4945f6..ccc18d3cb 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -44,11 +44,13 @@ DEFAULT ROOT PRIVILEGED + AURORA @string/pref_installation_method_default @string/pref_installation_method_root @string/pref_installation_method_privileged + @string/pref_installation_method_aurora 0 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 697fffdfa..a32c37214 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -298,6 +298,8 @@ Installation method Default installation dialog, no background installs Using system permissions + Using Aurora services + Aurora Services not installed Using root Attempt to set a fallback directory (%1$s)? Only available if Aurora is a system app