store Apk instance in Installer instance for fetching state
If F-Droid or InstallManagerService get killed while an install is in progress, that install will ultimately broadcast back to InstallManagerService to manage the notifications. The state is gone since things have been killed, so include the Apk instance in the Intent that is included in the broadcasts so that InstallManagerService can fetch all required info from the database. closes #698
This commit is contained in:
parent
fc5c41036d
commit
e69a6d5a8f
@ -862,7 +862,7 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case UNINSTALL:
|
case UNINSTALL:
|
||||||
uninstallApk(app.packageName);
|
uninstallApk();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case IGNOREALL:
|
case IGNOREALL:
|
||||||
@ -959,9 +959,13 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
InstallManagerService.queue(this, app, apk);
|
InstallManagerService.queue(this, app, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uninstallApk(String packageName) {
|
/**
|
||||||
Installer installer = InstallerFactory.create(this, null);
|
* Queue for uninstall based on the instance variable {@link #app}
|
||||||
Intent intent = installer.getUninstallScreen(packageName);
|
*/
|
||||||
|
private void uninstallApk() {
|
||||||
|
Apk apk = app.installedApk;
|
||||||
|
Installer installer = InstallerFactory.create(this, apk);
|
||||||
|
Intent intent = installer.getUninstallScreen(apk);
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
// uninstall screen required
|
// uninstall screen required
|
||||||
Utils.debugLog(TAG, "screen screen required");
|
Utils.debugLog(TAG, "screen screen required");
|
||||||
@ -975,7 +979,7 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
private void startUninstall() {
|
private void startUninstall() {
|
||||||
localBroadcastManager.registerReceiver(uninstallReceiver,
|
localBroadcastManager.registerReceiver(uninstallReceiver,
|
||||||
Installer.getUninstallIntentFilter(app.packageName));
|
Installer.getUninstallIntentFilter(app.packageName));
|
||||||
InstallerService.uninstall(context, app.packageName);
|
InstallerService.uninstall(context, app.installedApk);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchApk(String packageName) {
|
private void launchApk(String packageName) {
|
||||||
@ -1630,7 +1634,7 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
// If "launchable", launch
|
// If "launchable", launch
|
||||||
activity.launchApk(app.packageName);
|
activity.launchApk(app.packageName);
|
||||||
} else {
|
} else {
|
||||||
activity.uninstallApk(app.packageName);
|
activity.uninstallApk();
|
||||||
}
|
}
|
||||||
} else if (app.suggestedVersionCode > 0) {
|
} else if (app.suggestedVersionCode > 0) {
|
||||||
// If not installed, install
|
// If not installed, install
|
||||||
@ -1658,10 +1662,6 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
appDetails = (AppDetails) activity;
|
appDetails = (AppDetails) activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove() {
|
|
||||||
appDetails.uninstallApk(appDetails.getApp().packageName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
// A bit of a hack, but we can't add the header view in setupSummaryHeader(),
|
// A bit of a hack, but we can't add the header view in setupSummaryHeader(),
|
||||||
@ -1689,7 +1689,7 @@ public class AppDetails extends AppCompatActivity {
|
|||||||
App app = appDetails.getApp();
|
App app = appDetails.getApp();
|
||||||
final Apk apk = appDetails.getApks().getItem(position - l.getHeaderViewsCount());
|
final Apk apk = appDetails.getApks().getItem(position - l.getHeaderViewsCount());
|
||||||
if (app.installedVersionCode == apk.versionCode) {
|
if (app.installedVersionCode == apk.versionCode) {
|
||||||
remove();
|
appDetails.uninstallApk();
|
||||||
} else if (app.installedVersionCode > apk.versionCode) {
|
} else if (app.installedVersionCode > apk.versionCode) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
builder.setMessage(R.string.installDowngrade);
|
builder.setMessage(R.string.installDowngrade);
|
||||||
|
@ -483,7 +483,9 @@ public class RepoUpdater {
|
|||||||
}
|
}
|
||||||
if (repoPushRequest.versionCode == null
|
if (repoPushRequest.versionCode == null
|
||||||
|| repoPushRequest.versionCode == packageInfo.versionCode) {
|
|| repoPushRequest.versionCode == packageInfo.versionCode) {
|
||||||
InstallerService.uninstall(context, packageName);
|
Apk apk = ApkProvider.Helper.find(context, repoPushRequest.packageName,
|
||||||
|
packageInfo.versionCode);
|
||||||
|
InstallerService.uninstall(context, apk);
|
||||||
} else {
|
} else {
|
||||||
Utils.debugLog(TAG, "ignoring request based on versionCode:" + repoPushRequest);
|
Utils.debugLog(TAG, "ignoring request based on versionCode:" + repoPushRequest);
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@ public class DefaultInstaller extends Installer {
|
|||||||
|
|
||||||
private static final String TAG = "DefaultInstaller";
|
private static final String TAG = "DefaultInstaller";
|
||||||
|
|
||||||
DefaultInstaller(Context context) {
|
DefaultInstaller(Context context, Apk apk) {
|
||||||
super(context);
|
super(context, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -54,6 +54,7 @@ public class DefaultInstaller extends Installer {
|
|||||||
Intent installIntent = new Intent(context, DefaultInstallerActivity.class);
|
Intent installIntent = new Intent(context, DefaultInstallerActivity.class);
|
||||||
installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE);
|
installIntent.setAction(DefaultInstallerActivity.ACTION_INSTALL_PACKAGE);
|
||||||
installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
|
installIntent.putExtra(Installer.EXTRA_DOWNLOAD_URI, downloadUri);
|
||||||
|
installIntent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
installIntent.setData(localApkUri);
|
installIntent.setData(localApkUri);
|
||||||
|
|
||||||
PendingIntent installPendingIntent = PendingIntent.getActivity(
|
PendingIntent installPendingIntent = PendingIntent.getActivity(
|
||||||
@ -67,21 +68,19 @@ public class DefaultInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallPackage(String packageName) {
|
protected void uninstallPackage() {
|
||||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
|
||||||
|
|
||||||
Intent uninstallIntent = new Intent(context, DefaultInstallerActivity.class);
|
Intent uninstallIntent = new Intent(context, DefaultInstallerActivity.class);
|
||||||
uninstallIntent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE);
|
uninstallIntent.setAction(DefaultInstallerActivity.ACTION_UNINSTALL_PACKAGE);
|
||||||
uninstallIntent.putExtra(
|
uninstallIntent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
DefaultInstallerActivity.EXTRA_UNINSTALL_PACKAGE_NAME, packageName);
|
|
||||||
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
|
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
|
||||||
context.getApplicationContext(),
|
context.getApplicationContext(),
|
||||||
packageName.hashCode(),
|
apk.packageName.hashCode(),
|
||||||
uninstallIntent,
|
uninstallIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
sendBroadcastUninstall(packageName,
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
||||||
Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,6 +31,7 @@ import android.support.v4.app.FragmentActivity;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transparent activity as a wrapper around Android's PackageInstaller Intents
|
* A transparent activity as a wrapper around Android's PackageInstaller Intents
|
||||||
@ -41,13 +42,10 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
static final String ACTION_INSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.INSTALL_PACKAGE";
|
static final String ACTION_INSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.INSTALL_PACKAGE";
|
||||||
static final String ACTION_UNINSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.UNINSTALL_PACKAGE";
|
static final String ACTION_UNINSTALL_PACKAGE = "org.fdroid.fdroid.installer.DefaultInstaller.action.UNINSTALL_PACKAGE";
|
||||||
|
|
||||||
static final String EXTRA_UNINSTALL_PACKAGE_NAME = "org.fdroid.fdroid.installer.DefaultInstaller.extra.UNINSTALL_PACKAGE_NAME";
|
|
||||||
|
|
||||||
private static final int REQUEST_CODE_INSTALL = 0;
|
private static final int REQUEST_CODE_INSTALL = 0;
|
||||||
private static final int REQUEST_CODE_UNINSTALL = 1;
|
private static final int REQUEST_CODE_UNINSTALL = 1;
|
||||||
|
|
||||||
private Uri downloadUri;
|
private Uri downloadUri;
|
||||||
private String uninstallPackageName;
|
|
||||||
|
|
||||||
// for the broadcasts
|
// for the broadcasts
|
||||||
private DefaultInstaller installer;
|
private DefaultInstaller installer;
|
||||||
@ -56,18 +54,16 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
installer = new DefaultInstaller(this);
|
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
|
Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
|
installer = new DefaultInstaller(this, apk);
|
||||||
if (ACTION_INSTALL_PACKAGE.equals(action)) {
|
if (ACTION_INSTALL_PACKAGE.equals(action)) {
|
||||||
Uri localApkUri = intent.getData();
|
Uri localApkUri = intent.getData();
|
||||||
downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
||||||
installPackage(localApkUri);
|
installPackage(localApkUri);
|
||||||
} else if (ACTION_UNINSTALL_PACKAGE.equals(action)) {
|
} else if (ACTION_UNINSTALL_PACKAGE.equals(action)) {
|
||||||
uninstallPackageName = intent.getStringExtra(EXTRA_UNINSTALL_PACKAGE_NAME);
|
uninstallPackage(apk.packageName);
|
||||||
|
|
||||||
uninstallPackage(uninstallPackageName);
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Intent action not specified!");
|
throw new IllegalStateException("Intent action not specified!");
|
||||||
}
|
}
|
||||||
@ -134,7 +130,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
getPackageManager().getPackageInfo(packageName, 0);
|
getPackageManager().getPackageInfo(packageName, 0);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
Log.e(TAG, "NameNotFoundException", e);
|
Log.e(TAG, "NameNotFoundException", e);
|
||||||
installer.sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_INTERRUPTED,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
|
||||||
"Package that is scheduled for uninstall is not installed!");
|
"Package that is scheduled for uninstall is not installed!");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
@ -155,7 +151,7 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
startActivityForResult(intent, REQUEST_CODE_UNINSTALL);
|
startActivityForResult(intent, REQUEST_CODE_UNINSTALL);
|
||||||
} catch (ActivityNotFoundException e) {
|
} catch (ActivityNotFoundException e) {
|
||||||
Log.e(TAG, "ActivityNotFoundException", e);
|
Log.e(TAG, "ActivityNotFoundException", e);
|
||||||
installer.sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_INTERRUPTED,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
|
||||||
"This Android rom does not support ACTION_UNINSTALL_PACKAGE!");
|
"This Android rom does not support ACTION_UNINSTALL_PACKAGE!");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@ -197,25 +193,21 @@ public class DefaultInstallerActivity extends FragmentActivity {
|
|||||||
case REQUEST_CODE_UNINSTALL:
|
case REQUEST_CODE_UNINSTALL:
|
||||||
// resultCode is always 0 on Android < 4.0.
|
// resultCode is always 0 on Android < 4.0.
|
||||||
if (Build.VERSION.SDK_INT < 14) {
|
if (Build.VERSION.SDK_INT < 14) {
|
||||||
installer.sendBroadcastUninstall(uninstallPackageName,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
|
||||||
Installer.ACTION_UNINSTALL_COMPLETE);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case Activity.RESULT_OK:
|
case Activity.RESULT_OK:
|
||||||
installer.sendBroadcastUninstall(uninstallPackageName,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
|
||||||
Installer.ACTION_UNINSTALL_COMPLETE);
|
|
||||||
break;
|
break;
|
||||||
case Activity.RESULT_CANCELED:
|
case Activity.RESULT_CANCELED:
|
||||||
installer.sendBroadcastUninstall(uninstallPackageName,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED);
|
||||||
Installer.ACTION_UNINSTALL_INTERRUPTED);
|
|
||||||
break;
|
break;
|
||||||
case Activity.RESULT_FIRST_USER:
|
case Activity.RESULT_FIRST_USER:
|
||||||
default:
|
default:
|
||||||
// AOSP UninstallAppProgress returns RESULT_FIRST_USER on error
|
// AOSP UninstallAppProgress returns RESULT_FIRST_USER on error
|
||||||
installer.sendBroadcastUninstall(uninstallPackageName,
|
installer.sendBroadcastUninstall(Installer.ACTION_UNINSTALL_INTERRUPTED,
|
||||||
Installer.ACTION_UNINSTALL_INTERRUPTED,
|
|
||||||
getString(R.string.uninstall_error_unknown));
|
getString(R.string.uninstall_error_unknown));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2016 Blue Jay Wireless
|
||||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -39,8 +40,8 @@ import java.io.File;
|
|||||||
*/
|
*/
|
||||||
public class ExtensionInstaller extends Installer {
|
public class ExtensionInstaller extends Installer {
|
||||||
|
|
||||||
ExtensionInstaller(Context context) {
|
ExtensionInstaller(Context context, Apk apk) {
|
||||||
super(context);
|
super(context, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -71,23 +72,22 @@ public class ExtensionInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallPackage(String packageName) {
|
protected void uninstallPackage() {
|
||||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
|
||||||
|
|
||||||
Intent uninstallIntent = new Intent(context, InstallExtensionDialogActivity.class);
|
Intent uninstallIntent = new Intent(context, InstallExtensionDialogActivity.class);
|
||||||
uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL);
|
uninstallIntent.setAction(InstallExtensionDialogActivity.ACTION_UNINSTALL);
|
||||||
|
|
||||||
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
|
PendingIntent uninstallPendingIntent = PendingIntent.getActivity(
|
||||||
context.getApplicationContext(),
|
context.getApplicationContext(),
|
||||||
packageName.hashCode(),
|
apk.packageName.hashCode(),
|
||||||
uninstallIntent,
|
uninstallIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
sendBroadcastUninstall(packageName,
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
||||||
Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
|
|
||||||
|
|
||||||
// don't use broadcasts for the rest of this special installer
|
// don't use broadcasts for the rest of this special installer
|
||||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_COMPLETE);
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_COMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -235,6 +235,7 @@ public class InstallManagerService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String downloadUrl = intent.getDataString();
|
String downloadUrl = intent.getDataString();
|
||||||
|
Apk apk;
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Installer.ACTION_INSTALL_STARTED:
|
case Installer.ACTION_INSTALL_STARTED:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
@ -247,6 +248,7 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
break;
|
break;
|
||||||
case Installer.ACTION_INSTALL_INTERRUPTED:
|
case Installer.ACTION_INSTALL_INTERRUPTED:
|
||||||
|
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
String errorMessage =
|
String errorMessage =
|
||||||
intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE);
|
intent.getStringExtra(Installer.EXTRA_ERROR_MESSAGE);
|
||||||
|
|
||||||
@ -264,15 +266,15 @@ public class InstallManagerService extends Service {
|
|||||||
localBroadcastManager.unregisterReceiver(this);
|
localBroadcastManager.unregisterReceiver(this);
|
||||||
break;
|
break;
|
||||||
case Installer.ACTION_INSTALL_USER_INTERACTION:
|
case Installer.ACTION_INSTALL_USER_INTERACTION:
|
||||||
|
apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
PendingIntent installPendingIntent =
|
PendingIntent installPendingIntent =
|
||||||
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
||||||
|
|
||||||
Apk apkUserInteraction = getApkFromActive(downloadUrl);
|
|
||||||
// show notification if app details is not visible
|
// show notification if app details is not visible
|
||||||
if (AppDetails.isAppVisible(apkUserInteraction.packageName)) {
|
if (AppDetails.isAppVisible(apk.packageName)) {
|
||||||
cancelNotification(downloadUrl);
|
cancelNotification(downloadUrl);
|
||||||
} else {
|
} else {
|
||||||
notifyDownloadComplete(apkUserInteraction, downloadUrl, installPendingIntent);
|
notifyDownloadComplete(apk, downloadUrl, installPendingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2016 Blue Jay Wireless
|
||||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -42,11 +43,12 @@ import java.io.IOException;
|
|||||||
* Handles the actual install process. Subclasses implement the details.
|
* Handles the actual install process. Subclasses implement the details.
|
||||||
*/
|
*/
|
||||||
public abstract class Installer {
|
public abstract class Installer {
|
||||||
final Context context;
|
|
||||||
private final LocalBroadcastManager localBroadcastManager;
|
|
||||||
|
|
||||||
private static final String TAG = "Installer";
|
private static final String TAG = "Installer";
|
||||||
|
|
||||||
|
final Context context;
|
||||||
|
final Apk apk;
|
||||||
|
private final LocalBroadcastManager localBroadcastManager;
|
||||||
|
|
||||||
public static final String ACTION_INSTALL_STARTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_STARTED";
|
public static final String ACTION_INSTALL_STARTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_STARTED";
|
||||||
public static final String ACTION_INSTALL_COMPLETE = "org.fdroid.fdroid.installer.Installer.action.INSTALL_COMPLETE";
|
public static final String ACTION_INSTALL_COMPLETE = "org.fdroid.fdroid.installer.Installer.action.INSTALL_COMPLETE";
|
||||||
public static final String ACTION_INSTALL_INTERRUPTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_INTERRUPTED";
|
public static final String ACTION_INSTALL_INTERRUPTED = "org.fdroid.fdroid.installer.Installer.action.INSTALL_INTERRUPTED";
|
||||||
@ -67,12 +69,16 @@ public abstract class Installer {
|
|||||||
*/
|
*/
|
||||||
static final String EXTRA_DOWNLOAD_URI = "org.fdroid.fdroid.installer.Installer.extra.DOWNLOAD_URI";
|
static final String EXTRA_DOWNLOAD_URI = "org.fdroid.fdroid.installer.Installer.extra.DOWNLOAD_URI";
|
||||||
public static final String EXTRA_APK = "org.fdroid.fdroid.installer.Installer.extra.APK";
|
public static final String EXTRA_APK = "org.fdroid.fdroid.installer.Installer.extra.APK";
|
||||||
public static final String EXTRA_PACKAGE_NAME = "org.fdroid.fdroid.installer.Installer.extra.PACKAGE_NAME";
|
|
||||||
public static final String EXTRA_USER_INTERACTION_PI = "org.fdroid.fdroid.installer.Installer.extra.USER_INTERACTION_PI";
|
public static final String EXTRA_USER_INTERACTION_PI = "org.fdroid.fdroid.installer.Installer.extra.USER_INTERACTION_PI";
|
||||||
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.installer.Installer.extra.ERROR_MESSAGE";
|
public static final String EXTRA_ERROR_MESSAGE = "org.fdroid.fdroid.net.installer.Installer.extra.ERROR_MESSAGE";
|
||||||
|
|
||||||
Installer(Context context) {
|
/**
|
||||||
|
* @param apk must be included so that all the phases of the install process
|
||||||
|
* can get all the data about the app, even after F-Droid was killed
|
||||||
|
*/
|
||||||
|
Installer(Context context, Apk apk) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.apk = apk;
|
||||||
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,18 +131,18 @@ public abstract class Installer {
|
|||||||
* Returns an Intent to start a dialog wrapped in an activity
|
* Returns an Intent to start a dialog wrapped in an activity
|
||||||
* for uninstall confirmation.
|
* for uninstall confirmation.
|
||||||
*
|
*
|
||||||
* @param packageName packageName of app to uninstall
|
* @param apk {@link Apk} instance of app to uninstall
|
||||||
* @return Intent with activity for uninstall confirmation
|
* @return Intent with activity for uninstall confirmation
|
||||||
* Returns null if Installer handles that on itself, e.g.,
|
* Returns null if Installer handles that on itself, e.g.,
|
||||||
* with DefaultInstaller.
|
* with DefaultInstaller.
|
||||||
*/
|
*/
|
||||||
public Intent getUninstallScreen(String packageName) {
|
public Intent getUninstallScreen(Apk apk) {
|
||||||
if (!isUnattended()) {
|
if (!isUnattended()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = new Intent(context, UninstallDialogActivity.class);
|
Intent intent = new Intent(context, UninstallDialogActivity.class);
|
||||||
intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
@ -158,31 +164,31 @@ public abstract class Installer {
|
|||||||
Intent intent = new Intent(action);
|
Intent intent = new Intent(action);
|
||||||
intent.setData(downloadUri);
|
intent.setData(downloadUri);
|
||||||
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
||||||
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
if (!TextUtils.isEmpty(errorMessage)) {
|
if (!TextUtils.isEmpty(errorMessage)) {
|
||||||
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
|
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
|
||||||
}
|
}
|
||||||
localBroadcastManager.sendBroadcast(intent);
|
localBroadcastManager.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastUninstall(String packageName, String action, String errorMessage) {
|
void sendBroadcastUninstall(String action, String errorMessage) {
|
||||||
sendBroadcastUninstall(packageName, action, null, errorMessage);
|
sendBroadcastUninstall(action, null, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastUninstall(String packageName, String action) {
|
void sendBroadcastUninstall(String action) {
|
||||||
sendBroadcastUninstall(packageName, action, null, null);
|
sendBroadcastUninstall(action, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastUninstall(String packageName, String action, PendingIntent pendingIntent) {
|
void sendBroadcastUninstall(String action, PendingIntent pendingIntent) {
|
||||||
sendBroadcastUninstall(packageName, action, pendingIntent, null);
|
sendBroadcastUninstall(action, pendingIntent, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendBroadcastUninstall(String packageName, String action,
|
void sendBroadcastUninstall(String action, PendingIntent pendingIntent, String errorMessage) {
|
||||||
PendingIntent pendingIntent, String errorMessage) {
|
Uri uri = Uri.fromParts("package", apk.packageName, null);
|
||||||
Uri uri = Uri.fromParts("package", packageName, null);
|
|
||||||
|
|
||||||
Intent intent = new Intent(action);
|
Intent intent = new Intent(action);
|
||||||
intent.setData(uri); // for broadcast filtering
|
intent.setData(uri); // for broadcast filtering
|
||||||
intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
intent.putExtra(Installer.EXTRA_USER_INTERACTION_PI, pendingIntent);
|
||||||
if (!TextUtils.isEmpty(errorMessage)) {
|
if (!TextUtils.isEmpty(errorMessage)) {
|
||||||
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
|
intent.putExtra(Installer.EXTRA_ERROR_MESSAGE, errorMessage);
|
||||||
@ -242,7 +248,7 @@ public abstract class Installer {
|
|||||||
if (isUnattended()) {
|
if (isUnattended()) {
|
||||||
Log.e(TAG, e.getMessage(), e);
|
Log.e(TAG, e.getMessage(), e);
|
||||||
Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
|
Log.e(TAG, "Falling back to AOSP DefaultInstaller!");
|
||||||
DefaultInstaller defaultInstaller = new DefaultInstaller(context);
|
DefaultInstaller defaultInstaller = new DefaultInstaller(context, apk);
|
||||||
defaultInstaller.installPackageInternal(localApkUri, downloadUri, apk);
|
defaultInstaller.installPackageInternal(localApkUri, downloadUri, apk);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -266,11 +272,10 @@ public abstract class Installer {
|
|||||||
protected abstract void installPackageInternal(Uri localApkUri, Uri downloadUri, Apk apk);
|
protected abstract void installPackageInternal(Uri localApkUri, Uri downloadUri, Apk apk);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uninstall app
|
* Uninstall app as defined by {@link Installer#apk} in
|
||||||
*
|
* {@link Installer#Installer(Context, Apk)}
|
||||||
* @param packageName package name of the app that should be uninstalled
|
|
||||||
*/
|
*/
|
||||||
protected abstract void uninstallPackage(String packageName);
|
protected abstract void uninstallPackage();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This {@link Installer} instance is capable of "unattended" install and
|
* This {@link Installer} instance is capable of "unattended" install and
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2016 Blue Jay Wireless
|
||||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -20,6 +21,7 @@
|
|||||||
package org.fdroid.fdroid.installer;
|
package org.fdroid.fdroid.installer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
@ -34,22 +36,23 @@ public class InstallerFactory {
|
|||||||
* case to install the "F-Droid Privileged Extension" ExtensionInstaller.
|
* case to install the "F-Droid Privileged Extension" ExtensionInstaller.
|
||||||
*
|
*
|
||||||
* @param context current {@link Context}
|
* @param context current {@link Context}
|
||||||
* @param apk apk to be installed. Required to select the ExtensionInstaller.
|
* @param apk to be installed, always required.
|
||||||
* If this is null, the ExtensionInstaller will never be returned.
|
|
||||||
* @return instance of an Installer
|
* @return instance of an Installer
|
||||||
*/
|
*/
|
||||||
public static Installer create(Context context, Apk apk) {
|
public static Installer create(Context context, Apk apk) {
|
||||||
Installer installer;
|
if (apk == null || TextUtils.isEmpty(apk.packageName)) {
|
||||||
|
throw new IllegalArgumentException("packageName must not be empty!");
|
||||||
|
}
|
||||||
|
|
||||||
if (apk != null
|
Installer installer;
|
||||||
&& apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) {
|
if (apk.packageName.equals(PrivilegedInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME)) {
|
||||||
// special case for "F-Droid Privileged Extension"
|
// special case for "F-Droid Privileged Extension"
|
||||||
installer = new ExtensionInstaller(context);
|
installer = new ExtensionInstaller(context, apk);
|
||||||
} else if (PrivilegedInstaller.isDefault(context)) {
|
} else if (PrivilegedInstaller.isDefault(context)) {
|
||||||
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
|
Utils.debugLog(TAG, "privileged extension correctly installed -> PrivilegedInstaller");
|
||||||
installer = new PrivilegedInstaller(context);
|
installer = new PrivilegedInstaller(context, apk);
|
||||||
} else {
|
} else {
|
||||||
installer = new DefaultInstaller(context);
|
installer = new DefaultInstaller(context, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
return installer;
|
return installer;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2016 Blue Jay Wireless
|
||||||
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@ -65,8 +66,7 @@ public class InstallerService extends IntentService {
|
|||||||
Uri downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
Uri downloadUri = intent.getParcelableExtra(Installer.EXTRA_DOWNLOAD_URI);
|
||||||
installer.installPackage(uri, downloadUri, apk);
|
installer.installPackage(uri, downloadUri, apk);
|
||||||
} else if (ACTION_UNINSTALL.equals(intent.getAction())) {
|
} else if (ACTION_UNINSTALL.equals(intent.getAction())) {
|
||||||
String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
|
installer.uninstallPackage();
|
||||||
installer.uninstallPackage(packageName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,13 +90,13 @@ public class InstallerService extends IntentService {
|
|||||||
/**
|
/**
|
||||||
* Uninstall an app
|
* Uninstall an app
|
||||||
*
|
*
|
||||||
* @param context this app's {@link Context}
|
* @param context this app's {@link Context}
|
||||||
* @param packageName package name of the app that will be uninstalled
|
* @param apk {@link Apk} instance of the app that will be uninstalled
|
||||||
*/
|
*/
|
||||||
public static void uninstall(Context context, String packageName) {
|
public static void uninstall(Context context, Apk apk) {
|
||||||
Intent intent = new Intent(context, InstallerService.class);
|
Intent intent = new Intent(context, InstallerService.class);
|
||||||
intent.setAction(ACTION_UNINSTALL);
|
intent.setAction(ACTION_UNINSTALL);
|
||||||
intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
intent.putExtra(Installer.EXTRA_APK, apk);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright (C) 2016 Blue Jay Wireless
|
||||||
* Copyright (C) 2014-2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
* Copyright (C) 2014-2016 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||||
* Copyright (C) 2015 Daniel Martí <mvdan@mvdan.cc>
|
* Copyright (C) 2015 Daniel Martí <mvdan@mvdan.cc>
|
||||||
*
|
*
|
||||||
@ -255,8 +256,8 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
"device owner has marked the package as uninstallable.");
|
"device owner has marked the package as uninstallable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public PrivilegedInstaller(Context context) {
|
public PrivilegedInstaller(Context context, Apk apk) {
|
||||||
super(context);
|
super(context, apk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExtensionInstalled(Context context) {
|
public static boolean isExtensionInstalled(Context context) {
|
||||||
@ -354,8 +355,8 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void uninstallPackage(final String packageName) {
|
protected void uninstallPackage() {
|
||||||
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
|
sendBroadcastUninstall(Installer.ACTION_UNINSTALL_STARTED);
|
||||||
|
|
||||||
ServiceConnection mServiceConnection = new ServiceConnection() {
|
ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
@ -365,9 +366,9 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
@Override
|
@Override
|
||||||
public void handleResult(String packageName, int returnCode) throws RemoteException {
|
public void handleResult(String packageName, int returnCode) throws RemoteException {
|
||||||
if (returnCode == DELETE_SUCCEEDED) {
|
if (returnCode == DELETE_SUCCEEDED) {
|
||||||
sendBroadcastUninstall(packageName, ACTION_UNINSTALL_COMPLETE);
|
sendBroadcastUninstall(ACTION_UNINSTALL_COMPLETE);
|
||||||
} else {
|
} else {
|
||||||
sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
|
sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
|
||||||
"Error " + returnCode + ": "
|
"Error " + returnCode + ": "
|
||||||
+ UNINSTALL_RETURN_CODES.get(returnCode));
|
+ UNINSTALL_RETURN_CODES.get(returnCode));
|
||||||
}
|
}
|
||||||
@ -377,15 +378,15 @@ public class PrivilegedInstaller extends Installer {
|
|||||||
try {
|
try {
|
||||||
boolean hasPermissions = privService.hasPrivilegedPermissions();
|
boolean hasPermissions = privService.hasPrivilegedPermissions();
|
||||||
if (!hasPermissions) {
|
if (!hasPermissions) {
|
||||||
sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
|
sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
|
||||||
context.getString(R.string.system_install_denied_permissions));
|
context.getString(R.string.system_install_denied_permissions));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
privService.deletePackage(packageName, 0, callback);
|
privService.deletePackage(apk.packageName, 0, callback);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "RemoteException", e);
|
Log.e(TAG, "RemoteException", e);
|
||||||
sendBroadcastUninstall(packageName, ACTION_UNINSTALL_INTERRUPTED,
|
sendBroadcastUninstall(ACTION_UNINSTALL_INTERRUPTED,
|
||||||
"connecting to privileged service failed");
|
"connecting to privileged service failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import android.view.ContextThemeWrapper;
|
|||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
import org.fdroid.fdroid.R;
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.installer.Installer;
|
import org.fdroid.fdroid.installer.Installer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,14 +49,15 @@ public class UninstallDialogActivity extends FragmentActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
final Intent intent = getIntent();
|
final Intent intent = getIntent();
|
||||||
final String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
|
final Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||||
|
|
||||||
PackageManager pm = getPackageManager();
|
PackageManager pm = getPackageManager();
|
||||||
|
|
||||||
ApplicationInfo appInfo;
|
ApplicationInfo appInfo;
|
||||||
try {
|
try {
|
||||||
//noinspection WrongConstant (lint is actually wrong here!)
|
//noinspection WrongConstant (lint is actually wrong here!)
|
||||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
|
appInfo = pm.getApplicationInfo(apk.packageName,
|
||||||
|
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
throw new RuntimeException("Failed to get ApplicationInfo for uninstalling");
|
throw new RuntimeException("Failed to get ApplicationInfo for uninstalling");
|
||||||
}
|
}
|
||||||
@ -86,7 +88,7 @@ public class UninstallDialogActivity extends FragmentActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
Intent data = new Intent();
|
Intent data = new Intent();
|
||||||
data.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
|
data.putExtra(Installer.EXTRA_APK, apk);
|
||||||
setResult(Activity.RESULT_OK, intent);
|
setResult(Activity.RESULT_OK, intent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user