Installation/Delete with root shell, add preference for root access, fixes to Installer classes
This commit is contained in:
parent
8f08289822
commit
7ed69c93fc
@ -28,6 +28,9 @@
|
|||||||
<string name="notify_off">Do not notify of any updates</string>
|
<string name="notify_off">Do not notify of any updates</string>
|
||||||
<string name="update_history">Update history</string>
|
<string name="update_history">Update history</string>
|
||||||
<string name="update_history_summ">Days to consider apps new or recent: %s</string>
|
<string name="update_history_summ">Days to consider apps new or recent: %s</string>
|
||||||
|
<string name="root_installer">Root access for app installations</string>
|
||||||
|
<string name="root_installer_on">Root access is used to install/delete/update applications</string>
|
||||||
|
<string name="root_installer_off">Do not request root access to install/delete/update applications</string>
|
||||||
|
|
||||||
<string name="search_results">Search Results</string>
|
<string name="search_results">Search Results</string>
|
||||||
<string name="app_details">App Details</string>
|
<string name="app_details">App Details</string>
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
<PreferenceCategory android:title="@string/updates">
|
<PreferenceCategory android:title="@string/updates">
|
||||||
<ListPreference android:title="@string/update_interval"
|
<ListPreference android:title="@string/update_interval"
|
||||||
android:key="updateInterval"
|
android:key="updateInterval"
|
||||||
android:defaultValue="24" android:entries="@array/updateIntervalNames"
|
android:defaultValue="24"
|
||||||
|
android:entries="@array/updateIntervalNames"
|
||||||
android:entryValues="@array/updateIntervalValues" />
|
android:entryValues="@array/updateIntervalValues" />
|
||||||
<CheckBoxPreference android:title="@string/automatic_scan_wifi"
|
<CheckBoxPreference android:title="@string/automatic_scan_wifi"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
@ -17,6 +18,9 @@
|
|||||||
android:maxLength="2"
|
android:maxLength="2"
|
||||||
android:numeric="integer"
|
android:numeric="integer"
|
||||||
android:title="@string/update_history" />
|
android:title="@string/update_history" />
|
||||||
|
<CheckBoxPreference android:title="@string/root_installer"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="rootInstaller" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/display">
|
<PreferenceCategory android:title="@string/display">
|
||||||
<CheckBoxPreference android:title="@string/showPermissions"
|
<CheckBoxPreference android:title="@string/showPermissions"
|
||||||
|
@ -310,7 +310,7 @@ public class AppDetails extends ListActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPm = getPackageManager();
|
mPm = getPackageManager();
|
||||||
installer = Installer.getActivityInstaller(this, mPm, myInstallCallback);
|
installer = Installer.getActivityInstaller(this, mPm, myInstallerCallback);
|
||||||
|
|
||||||
// Get the preferences we're going to use in this Activity...
|
// Get the preferences we're going to use in this Activity...
|
||||||
AppDetails old = (AppDetails) getLastNonConfigurationInstance();
|
AppDetails old = (AppDetails) getLastNonConfigurationInstance();
|
||||||
@ -957,7 +957,7 @@ public class AppDetails extends ListActivity {
|
|||||||
getContentResolver().notifyChange(AppProvider.getContentUri(id), null);
|
getContentResolver().notifyChange(AppProvider.getContentUri(id), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Installer.InstallerCallback myInstallCallback = new Installer.InstallerCallback() {
|
private Installer.InstallerCallback myInstallerCallback = new Installer.InstallerCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPackageInstalled(int returnCode, boolean unattended) {
|
public void onPackageInstalled(int returnCode, boolean unattended) {
|
||||||
@ -969,26 +969,14 @@ public class AppDetails extends ListActivity {
|
|||||||
PackageManagerCompat.setInstaller(mPm, app.id);
|
PackageManagerCompat.setInstaller(mPm, app.id);
|
||||||
resetRequired = true;
|
resetRequired = true;
|
||||||
|
|
||||||
// TODO: Somehow the internal API needs time to update the package state.
|
// if unattended, onResume is not execute automatically
|
||||||
// This needs to be done nicer!
|
|
||||||
if (unattended) {
|
if (unattended) {
|
||||||
Thread wait = new Thread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
onResume();
|
||||||
Thread.sleep(200);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Log.d("FDroid", "resume");
|
|
||||||
onResume();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wait.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,26 +985,14 @@ public class AppDetails extends ListActivity {
|
|||||||
// TODO: check return code?!
|
// TODO: check return code?!
|
||||||
resetRequired = true;
|
resetRequired = true;
|
||||||
|
|
||||||
// TODO: Somehow the internal API needs time to update the package state.
|
// if unattended, onResume is not execute automatically
|
||||||
// This needs to be done nicer!
|
|
||||||
if (unattended) {
|
if (unattended) {
|
||||||
Thread wait = new Thread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
onResume();
|
||||||
Thread.sleep(200);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Log.d("FDroid", "resume");
|
|
||||||
onResume();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wait.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -36,10 +36,12 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
public static final String PREF_CACHE_APK = "cacheDownloaded";
|
public static final String PREF_CACHE_APK = "cacheDownloaded";
|
||||||
public static final String PREF_EXPERT = "expert";
|
public static final String PREF_EXPERT = "expert";
|
||||||
public static final String PREF_UPD_LAST = "lastUpdateCheck";
|
public static final String PREF_UPD_LAST = "lastUpdateCheck";
|
||||||
|
public static final String PREF_ROOT_INSTALLER = "rootInstaller";
|
||||||
|
|
||||||
private static final boolean DEFAULT_COMPACT_LAYOUT = false;
|
private static final boolean DEFAULT_COMPACT_LAYOUT = false;
|
||||||
private static final boolean DEFAULT_ROOTED = true;
|
private static final boolean DEFAULT_ROOTED = true;
|
||||||
private static final int DEFAULT_UPD_HISTORY = 14;
|
private static final int DEFAULT_UPD_HISTORY = 14;
|
||||||
|
private static final boolean DEFAULT_ROOT_INSTALLER = false;
|
||||||
|
|
||||||
private boolean compactLayout = DEFAULT_COMPACT_LAYOUT;
|
private boolean compactLayout = DEFAULT_COMPACT_LAYOUT;
|
||||||
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
||||||
@ -62,6 +64,10 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
initialized.put(key, false);
|
initialized.put(key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean useRootInstaller() {
|
||||||
|
return preferences.getBoolean(PREF_ROOT_INSTALLER, DEFAULT_ROOT_INSTALLER);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasCompactLayout() {
|
public boolean hasCompactLayout() {
|
||||||
if (!isInitialized(PREF_COMPACT_LAYOUT)) {
|
if (!isInitialized(PREF_COMPACT_LAYOUT)) {
|
||||||
initialize(PREF_COMPACT_LAYOUT);
|
initialize(PREF_COMPACT_LAYOUT);
|
||||||
@ -97,7 +103,7 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This is cached as it is called several times inside the AppListAdapter.
|
* This is cached as it is called several times inside the AppListAdapter.
|
||||||
* Providing it here means sthe shared preferences file only needs to be
|
* Providing it here means the shared preferences file only needs to be
|
||||||
* read once, and we will keep our copy up to date by listening to changes
|
* read once, and we will keep our copy up to date by listening to changes
|
||||||
* in PREF_ROOTED.
|
* in PREF_ROOTED.
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,8 @@ public class PreferencesActivity extends PreferenceActivity implements
|
|||||||
Preferences.PREF_COMPACT_LAYOUT,
|
Preferences.PREF_COMPACT_LAYOUT,
|
||||||
Preferences.PREF_IGN_TOUCH,
|
Preferences.PREF_IGN_TOUCH,
|
||||||
Preferences.PREF_CACHE_APK,
|
Preferences.PREF_CACHE_APK,
|
||||||
Preferences.PREF_EXPERT
|
Preferences.PREF_EXPERT,
|
||||||
|
Preferences.PREF_ROOT_INSTALLER
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -148,6 +149,10 @@ public class PreferencesActivity extends PreferenceActivity implements
|
|||||||
onoffSummary(key, R.string.expert_on,
|
onoffSummary(key, R.string.expert_on,
|
||||||
R.string.expert_off);
|
R.string.expert_off);
|
||||||
|
|
||||||
|
} else if (key.equals(Preferences.PREF_ROOT_INSTALLER)) {
|
||||||
|
onoffSummary(key, R.string.root_installer_on,
|
||||||
|
R.string.root_installer_off);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,12 +50,12 @@ public class DefaultInstaller extends Installer {
|
|||||||
private static final int REQUEST_CODE_DELETE = 1;
|
private static final int REQUEST_CODE_DELETE = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void installPackage(File file) throws AndroidNotCompatibleException {
|
public void installPackage(File apkFile) throws AndroidNotCompatibleException {
|
||||||
super.installPackage(file);
|
super.installPackage(apkFile);
|
||||||
|
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction(android.content.Intent.ACTION_VIEW);
|
intent.setAction(android.content.Intent.ACTION_VIEW);
|
||||||
intent.setDataAndType(Uri.parse("file://" + file.getPath()),
|
intent.setDataAndType(Uri.parse("file://" + apkFile.getPath()),
|
||||||
"application/vnd.android.package-archive");
|
"application/vnd.android.package-archive");
|
||||||
extraNotUnknownSource(intent);
|
extraNotUnknownSource(intent);
|
||||||
try {
|
try {
|
||||||
|
@ -21,15 +21,20 @@ package org.fdroid.fdroid.installer;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.Preferences;
|
||||||
|
|
||||||
import android.Manifest.permission;
|
import android.Manifest.permission;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract Installer class. Also provides static methods to automatically
|
||||||
|
* instantiate a working Installer based on F-Droids granted permissions.
|
||||||
|
*/
|
||||||
abstract public class Installer {
|
abstract public class Installer {
|
||||||
protected Context mContext;
|
protected Context mContext;
|
||||||
protected PackageManager mPm;
|
protected PackageManager mPm;
|
||||||
@ -63,6 +68,10 @@ abstract public class Installer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback from Installer. NOTE: This callback can be in a different thread
|
||||||
|
* than the UI thread
|
||||||
|
*/
|
||||||
public interface InstallerCallback {
|
public interface InstallerCallback {
|
||||||
|
|
||||||
public static final int RETURN_SUCCESS = 1;
|
public static final int RETURN_SUCCESS = 1;
|
||||||
@ -93,6 +102,16 @@ abstract public class Installer {
|
|||||||
public static Installer getActivityInstaller(Activity activity, PackageManager pm,
|
public static Installer getActivityInstaller(Activity activity, PackageManager pm,
|
||||||
InstallerCallback callback) {
|
InstallerCallback callback) {
|
||||||
|
|
||||||
|
// if root installer has been activated in preferences -> RootInstaller
|
||||||
|
boolean useRootInstaller = Preferences.get().useRootInstaller();
|
||||||
|
if (useRootInstaller) {
|
||||||
|
try {
|
||||||
|
return new RootInstaller(activity, pm, callback);
|
||||||
|
} catch (AndroidNotCompatibleException e) {
|
||||||
|
Log.e(TAG, "Android not compatible with RootInstaller!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// system permissions -> SystemPermissionInstaller
|
// system permissions -> SystemPermissionInstaller
|
||||||
if (hasSystemPermissions(activity, pm)) {
|
if (hasSystemPermissions(activity, pm)) {
|
||||||
Log.d(TAG, "system permissions -> SystemPermissionInstaller");
|
Log.d(TAG, "system permissions -> SystemPermissionInstaller");
|
||||||
@ -104,7 +123,7 @@ abstract public class Installer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// try default installer
|
// Fallback -> DefaultInstaller
|
||||||
try {
|
try {
|
||||||
Log.d(TAG, "try default installer");
|
Log.d(TAG, "try default installer");
|
||||||
|
|
||||||
@ -122,6 +141,16 @@ abstract public class Installer {
|
|||||||
public static Installer getUnattendedInstaller(Context context, PackageManager pm,
|
public static Installer getUnattendedInstaller(Context context, PackageManager pm,
|
||||||
InstallerCallback callback) throws AndroidNotCompatibleException {
|
InstallerCallback callback) throws AndroidNotCompatibleException {
|
||||||
|
|
||||||
|
// if root installer has been activated in preferences -> RootInstaller
|
||||||
|
boolean useRootInstaller = Preferences.get().useRootInstaller();
|
||||||
|
if (useRootInstaller) {
|
||||||
|
try {
|
||||||
|
return new RootInstaller(context, pm, callback);
|
||||||
|
} catch (AndroidNotCompatibleException e) {
|
||||||
|
Log.e(TAG, "Android not compatible with RootInstaller!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasSystemPermissions(context, pm)) {
|
if (hasSystemPermissions(context, pm)) {
|
||||||
// we have system permissions!
|
// we have system permissions!
|
||||||
return new SystemPermissionInstaller(context, pm, callback);
|
return new SystemPermissionInstaller(context, pm, callback);
|
||||||
@ -139,29 +168,13 @@ abstract public class Installer {
|
|||||||
boolean permissionsGranted = (checkInstallPermission == PackageManager.PERMISSION_GRANTED
|
boolean permissionsGranted = (checkInstallPermission == PackageManager.PERMISSION_GRANTED
|
||||||
&& checkDeletePermission == PackageManager.PERMISSION_GRANTED);
|
&& checkDeletePermission == PackageManager.PERMISSION_GRANTED);
|
||||||
|
|
||||||
boolean isSystemApp;
|
if (permissionsGranted) {
|
||||||
try {
|
|
||||||
isSystemApp = isSystemApp(pm.getApplicationInfo(context.getPackageName(), 0));
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
isSystemApp = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this right???
|
|
||||||
// two ways to be able to get system permissions: somehow the
|
|
||||||
// permissions where actually granted on install or the app has been
|
|
||||||
// moved later to the system partition -> also access
|
|
||||||
if (permissionsGranted || isSystemApp) {
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSystemApp(ApplicationInfo ai) {
|
|
||||||
int mask = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
|
||||||
return (ai.flags & mask) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void installPackage(File apkFile) throws AndroidNotCompatibleException {
|
public void installPackage(File apkFile) throws AndroidNotCompatibleException {
|
||||||
// check if file exists...
|
// check if file exists...
|
||||||
if (!apkFile.exists()) {
|
if (!apkFile.exists()) {
|
||||||
|
203
src/org/fdroid/fdroid/installer/RootInstaller.java
Normal file
203
src/org/fdroid/fdroid/installer/RootInstaller.java
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 3
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.fdroid.fdroid.installer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.chainfire.libsuperuser.Shell;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installer using a root shell and "pm install", "pm uninstall" commands
|
||||||
|
*/
|
||||||
|
public class RootInstaller extends Installer {
|
||||||
|
|
||||||
|
Shell.Interactive rootSession;
|
||||||
|
|
||||||
|
public RootInstaller(Context context, PackageManager pm, InstallerCallback callback)
|
||||||
|
throws AndroidNotCompatibleException {
|
||||||
|
super(context, pm, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installPackage(final File apkFile) throws AndroidNotCompatibleException {
|
||||||
|
super.installPackage(apkFile);
|
||||||
|
|
||||||
|
Shell.Builder shellBuilder = new Shell.Builder()
|
||||||
|
.useSU()
|
||||||
|
.setWantSTDERR(true)
|
||||||
|
.setWatchdogTimeout(5)
|
||||||
|
.setMinimalLogging(true);
|
||||||
|
|
||||||
|
rootSession = shellBuilder.open(new Shell.OnCommandResultListener() {
|
||||||
|
|
||||||
|
// Callback to report whether the shell was successfully
|
||||||
|
// started up
|
||||||
|
@Override
|
||||||
|
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
|
||||||
|
if (exitCode != Shell.OnCommandResultListener.SHELL_RUNNING) {
|
||||||
|
// TODO
|
||||||
|
// wrong uid
|
||||||
|
// Shell.OnCommandResultListener.SHELL_WRONG_UID
|
||||||
|
// exec failed
|
||||||
|
// Shell.OnCommandResultListener.SHELL_EXEC_FAILED
|
||||||
|
|
||||||
|
// reportError("Error opening root shell: exitCode " +
|
||||||
|
// exitCode);
|
||||||
|
} else {
|
||||||
|
// Shell is up: send our first request
|
||||||
|
sendInstallCommand(apkFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePackage(final String packageName) throws AndroidNotCompatibleException {
|
||||||
|
super.deletePackage(packageName);
|
||||||
|
|
||||||
|
Shell.Builder shellBuilder = new Shell.Builder()
|
||||||
|
.useSU()
|
||||||
|
.setWantSTDERR(true)
|
||||||
|
.setWatchdogTimeout(5)
|
||||||
|
.setMinimalLogging(true);
|
||||||
|
|
||||||
|
rootSession = shellBuilder.open(new Shell.OnCommandResultListener() {
|
||||||
|
|
||||||
|
// Callback to report whether the shell was successfully
|
||||||
|
// started up
|
||||||
|
@Override
|
||||||
|
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
|
||||||
|
if (exitCode != Shell.OnCommandResultListener.SHELL_RUNNING) {
|
||||||
|
// TODO
|
||||||
|
// wrong uid
|
||||||
|
// Shell.OnCommandResultListener.SHELL_WRONG_UID
|
||||||
|
// exec failed
|
||||||
|
// Shell.OnCommandResultListener.SHELL_EXEC_FAILED
|
||||||
|
|
||||||
|
// reportError("Error opening root shell: exitCode " +
|
||||||
|
// exitCode);
|
||||||
|
} else {
|
||||||
|
// Shell is up: send our first request
|
||||||
|
sendDeleteCommand(packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
// no need to handle onActivityResult
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendInstallCommand(File apkFile) {
|
||||||
|
rootSession.addCommand("pm install -r " + apkFile.getAbsolutePath(), 0,
|
||||||
|
new Shell.OnCommandResultListener() {
|
||||||
|
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
|
||||||
|
// close su shell
|
||||||
|
rootSession.close();
|
||||||
|
|
||||||
|
if (exitCode < 0) {
|
||||||
|
// reportError("Error executing commands: exitCode "
|
||||||
|
// + exitCode);
|
||||||
|
mCallback.onPackageInstalled(InstallerCallback.RETURN_CANCEL, true);
|
||||||
|
} else {
|
||||||
|
// wait until Android's internal PackageManger has
|
||||||
|
// received the new package state
|
||||||
|
Thread wait = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallback.onPackageInstalled(InstallerCallback.RETURN_SUCCESS,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wait.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDeleteCommand(String packageName) {
|
||||||
|
rootSession.addCommand("pm uninstall " + packageName, 0,
|
||||||
|
new Shell.OnCommandResultListener() {
|
||||||
|
public void onCommandResult(int commandCode, int exitCode, List<String> output) {
|
||||||
|
// close su shell
|
||||||
|
rootSession.close();
|
||||||
|
|
||||||
|
if (exitCode < 0) {
|
||||||
|
// reportError("Error executing commands: exitCode "
|
||||||
|
// + exitCode);
|
||||||
|
mCallback.onPackageDeleted(InstallerCallback.RETURN_CANCEL, true);
|
||||||
|
} else {
|
||||||
|
// wait until Android's internal PackageManger has
|
||||||
|
// received the new package state
|
||||||
|
Thread wait = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallback.onPackageDeleted(InstallerCallback.RETURN_SUCCESS,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wait.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f] [--algo
|
||||||
|
* <algorithm name> --key <key-in-hex> --iv <IV-in-hex>] [--originating-uri
|
||||||
|
* <URI>] [--referrer <URI>] PATH
|
||||||
|
* <p/>
|
||||||
|
* pm install: installs a package to the system.
|
||||||
|
* <p/>
|
||||||
|
* Options:<br/>
|
||||||
|
* -l: install the package with FORWARD_LOCK.<br/>
|
||||||
|
* -r: reinstall an exisiting app, keeping its data.<br/>
|
||||||
|
* -t: allow test .apks to be installed.<br/>
|
||||||
|
* -i: specify the installer package name.<br/>
|
||||||
|
* -s: install package on sdcard.<br/>
|
||||||
|
* -f: install package on internal flash.<br/>
|
||||||
|
* -d: allow version code downgrade.<br/>
|
||||||
|
* <p/>
|
||||||
|
* pm uninstall [-k] PACKAGE
|
||||||
|
* <p/>
|
||||||
|
* pm uninstall: removes a package from the system.
|
||||||
|
* <p/>
|
||||||
|
* Options:<br/>
|
||||||
|
* -k: keep the data and cache directories around after package removal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
@ -82,7 +82,21 @@ public class SystemPermissionInstaller extends Installer {
|
|||||||
// TODO: propagate other return codes?
|
// TODO: propagate other return codes?
|
||||||
if (returnCode == INSTALL_SUCCEEDED) {
|
if (returnCode == INSTALL_SUCCEEDED) {
|
||||||
Log.d(TAG, "Install succeeded");
|
Log.d(TAG, "Install succeeded");
|
||||||
mCallback.onPackageInstalled(InstallerCallback.RETURN_SUCCESS, true);
|
|
||||||
|
// wait until Android's internal PackageManger has
|
||||||
|
// received the new package state
|
||||||
|
Thread wait = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallback.onPackageInstalled(InstallerCallback.RETURN_SUCCESS, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wait.start();
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Install failed: " + returnCode);
|
Log.d(TAG, "Install failed: " + returnCode);
|
||||||
mCallback.onPackageInstalled(InstallerCallback.RETURN_CANCEL, true);
|
mCallback.onPackageInstalled(InstallerCallback.RETURN_CANCEL, true);
|
||||||
@ -98,7 +112,21 @@ public class SystemPermissionInstaller extends Installer {
|
|||||||
// TODO: propagate other return codes?
|
// TODO: propagate other return codes?
|
||||||
if (returnCode == DELETE_SUCCEEDED) {
|
if (returnCode == DELETE_SUCCEEDED) {
|
||||||
Log.d(TAG, "Delete succeeded");
|
Log.d(TAG, "Delete succeeded");
|
||||||
mCallback.onPackageDeleted(InstallerCallback.RETURN_SUCCESS, true);
|
|
||||||
|
// wait until Android's internal PackageManger has
|
||||||
|
// received the new package state
|
||||||
|
Thread wait = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
mCallback.onPackageDeleted(InstallerCallback.RETURN_SUCCESS, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
wait.start();
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Delete failed: " + returnCode);
|
Log.d(TAG, "Delete failed: " + returnCode);
|
||||||
mCallback.onPackageDeleted(InstallerCallback.RETURN_CANCEL, true);
|
mCallback.onPackageDeleted(InstallerCallback.RETURN_CANCEL, true);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user