Installer: handle package parse errors, fixes #395
This commit is contained in:
parent
74c93907c5
commit
10171d9228
@ -295,6 +295,7 @@
|
|||||||
<string name="update_all">Update all</string>
|
<string name="update_all">Update all</string>
|
||||||
<string name="install_error_title">Install error</string>
|
<string name="install_error_title">Install error</string>
|
||||||
<string name="install_error_unknown">Failed to install due to an unknown error</string>
|
<string name="install_error_unknown">Failed to install due to an unknown error</string>
|
||||||
|
<string name="install_error_cannot_parse">An error occurred while parsing the package</string>
|
||||||
<string name="uninstall_error_title">Uninstall error</string>
|
<string name="uninstall_error_title">Uninstall error</string>
|
||||||
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
<string name="uninstall_error_unknown">Failed to uninstall due to an unknown error</string>
|
||||||
<string name="system_permission_denied_title">System permissions denied</string>
|
<string name="system_permission_denied_title">System permissions denied</string>
|
||||||
|
@ -909,15 +909,20 @@ public class AppDetails extends AppCompatActivity implements ProgressListener, A
|
|||||||
final int title, body;
|
final int title, body;
|
||||||
if (operation == InstallerCallback.OPERATION_INSTALL) {
|
if (operation == InstallerCallback.OPERATION_INSTALL) {
|
||||||
title = R.string.install_error_title;
|
title = R.string.install_error_title;
|
||||||
} else {
|
switch (errorCode) {
|
||||||
title = R.string.uninstall_error_title;
|
case ERROR_CODE_CANNOT_PARSE:
|
||||||
}
|
body = R.string.install_error_cannot_parse;
|
||||||
switch (errorCode) {
|
break;
|
||||||
default:
|
default: // ERROR_CODE_OTHER
|
||||||
if (operation == InstallerCallback.OPERATION_INSTALL) {
|
|
||||||
body = R.string.install_error_unknown;
|
body = R.string.install_error_unknown;
|
||||||
} else {
|
break;
|
||||||
body = R.string.uninstall_error_unknown;
|
}
|
||||||
|
} else { // InstallerCallback.OPERATION_DELETE
|
||||||
|
title = R.string.uninstall_error_title;
|
||||||
|
switch (errorCode) {
|
||||||
|
default: // ERROR_CODE_OTHER
|
||||||
|
body = R.string.install_error_unknown;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
|
@ -36,6 +36,15 @@ public class AppDiff {
|
|||||||
final String pkgPath = mPackageURI.getPath();
|
final String pkgPath = mPackageURI.getPath();
|
||||||
|
|
||||||
mPkgInfo = mPm.getPackageArchiveInfo(pkgPath, PackageManager.GET_PERMISSIONS);
|
mPkgInfo = mPm.getPackageArchiveInfo(pkgPath, PackageManager.GET_PERMISSIONS);
|
||||||
|
// We could not get the package info from the file. This means that we
|
||||||
|
// could not parse the file, which can happen if the file cannot be
|
||||||
|
// read or the minSdk is not satisfied.
|
||||||
|
// Since we can't return an error from a constructor, we refuse to
|
||||||
|
// continue. The caller must check if mPkgInfo is null to see if the
|
||||||
|
// AppDiff was initialised correctly.
|
||||||
|
if (mPkgInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mPkgInfo.applicationInfo.sourceDir = pkgPath;
|
mPkgInfo.applicationInfo.sourceDir = pkgPath;
|
||||||
mPkgInfo.applicationInfo.publicSourceDir = pkgPath;
|
mPkgInfo.applicationInfo.publicSourceDir = pkgPath;
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ import org.fdroid.fdroid.R;
|
|||||||
|
|
||||||
public class InstallConfirmActivity extends Activity implements OnCancelListener, OnClickListener {
|
public class InstallConfirmActivity extends Activity implements OnCancelListener, OnClickListener {
|
||||||
|
|
||||||
|
public static final int RESULT_CANNOT_PARSE = RESULT_FIRST_USER + 1;
|
||||||
|
|
||||||
private Intent intent;
|
private Intent intent;
|
||||||
|
|
||||||
PackageManager mPm;
|
PackageManager mPm;
|
||||||
@ -83,55 +85,54 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
|
|||||||
mScrollView = null;
|
mScrollView = null;
|
||||||
mOkCanInstall = false;
|
mOkCanInstall = false;
|
||||||
int msg = 0;
|
int msg = 0;
|
||||||
if (mAppDiff.mPkgInfo != null) {
|
AppSecurityPermissions perms = new AppSecurityPermissions(this, mAppDiff.mPkgInfo);
|
||||||
AppSecurityPermissions perms = new AppSecurityPermissions(this, mAppDiff.mPkgInfo);
|
final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
|
||||||
final int NP = perms.getPermissionCount(AppSecurityPermissions.WHICH_PERSONAL);
|
final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
|
||||||
final int ND = perms.getPermissionCount(AppSecurityPermissions.WHICH_DEVICE);
|
if (mAppDiff.mInstalledAppInfo != null) {
|
||||||
if (mAppDiff.mInstalledAppInfo != null) {
|
msg = (mAppDiff.mInstalledAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||||
msg = (mAppDiff.mInstalledAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
? R.string.install_confirm_update_system
|
||||||
? R.string.install_confirm_update_system
|
: R.string.install_confirm_update;
|
||||||
: R.string.install_confirm_update;
|
mScrollView = new CaffeinatedScrollView(this);
|
||||||
mScrollView = new CaffeinatedScrollView(this);
|
mScrollView.setFillViewport(true);
|
||||||
mScrollView.setFillViewport(true);
|
final boolean newPermissionsFound =
|
||||||
final boolean newPermissionsFound =
|
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
|
||||||
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
|
if (newPermissionsFound) {
|
||||||
if (newPermissionsFound) {
|
|
||||||
permVisible = true;
|
|
||||||
mScrollView.addView(perms.getPermissionsView(
|
|
||||||
AppSecurityPermissions.WHICH_NEW));
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException("This should not happen. No new permissions were found but InstallConfirmActivity has been started!");
|
|
||||||
}
|
|
||||||
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
|
|
||||||
getText(R.string.newPerms)), mScrollView);
|
|
||||||
} else {
|
|
||||||
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
|
|
||||||
findViewById(R.id.divider).setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
if (NP > 0 || ND > 0) {
|
|
||||||
permVisible = true;
|
permVisible = true;
|
||||||
LayoutInflater inflater = (LayoutInflater) getSystemService(
|
mScrollView.addView(perms.getPermissionsView(
|
||||||
Context.LAYOUT_INFLATER_SERVICE);
|
AppSecurityPermissions.WHICH_NEW));
|
||||||
View root = inflater.inflate(R.layout.permissions_list, null);
|
} else {
|
||||||
if (mScrollView == null) {
|
throw new RuntimeException("This should not happen. No new permissions were found but InstallConfirmActivity has been started!");
|
||||||
mScrollView = (CaffeinatedScrollView) root.findViewById(R.id.scrollview);
|
|
||||||
}
|
|
||||||
final ViewGroup privacyList = (ViewGroup) root.findViewById(R.id.privacylist);
|
|
||||||
if (NP > 0) {
|
|
||||||
privacyList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
|
|
||||||
} else {
|
|
||||||
privacyList.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
final ViewGroup deviceList = (ViewGroup) root.findViewById(R.id.devicelist);
|
|
||||||
if (ND > 0) {
|
|
||||||
deviceList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
|
|
||||||
} else {
|
|
||||||
root.findViewById(R.id.devicelist).setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
|
|
||||||
getText(R.string.allPerms)), root);
|
|
||||||
}
|
}
|
||||||
|
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
|
||||||
|
getText(R.string.newPerms)), mScrollView);
|
||||||
|
} else {
|
||||||
|
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.divider).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
if (NP > 0 || ND > 0) {
|
||||||
|
permVisible = true;
|
||||||
|
LayoutInflater inflater = (LayoutInflater) getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
View root = inflater.inflate(R.layout.permissions_list, null);
|
||||||
|
if (mScrollView == null) {
|
||||||
|
mScrollView = (CaffeinatedScrollView) root.findViewById(R.id.scrollview);
|
||||||
|
}
|
||||||
|
final ViewGroup privacyList = (ViewGroup) root.findViewById(R.id.privacylist);
|
||||||
|
if (NP > 0) {
|
||||||
|
privacyList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_PERSONAL));
|
||||||
|
} else {
|
||||||
|
privacyList.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
final ViewGroup deviceList = (ViewGroup) root.findViewById(R.id.devicelist);
|
||||||
|
if (ND > 0) {
|
||||||
|
deviceList.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_DEVICE));
|
||||||
|
} else {
|
||||||
|
root.findViewById(R.id.devicelist).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
|
||||||
|
getText(R.string.allPerms)), root);
|
||||||
|
}
|
||||||
|
|
||||||
if (!permVisible) {
|
if (!permVisible) {
|
||||||
if (mAppDiff.mInstalledAppInfo != null) {
|
if (mAppDiff.mInstalledAppInfo != null) {
|
||||||
// This is an update to an application, but there are no
|
// This is an update to an application, but there are no
|
||||||
@ -184,6 +185,10 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
|
|||||||
Uri packageURI = intent.getData();
|
Uri packageURI = intent.getData();
|
||||||
|
|
||||||
mAppDiff = new AppDiff(mPm, packageURI);
|
mAppDiff = new AppDiff(mPm, packageURI);
|
||||||
|
if (mAppDiff.mPkgInfo == null) {
|
||||||
|
setResult(RESULT_CANNOT_PARSE, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
setContentView(R.layout.install_start);
|
setContentView(R.layout.install_start);
|
||||||
mInstallConfirm = findViewById(R.id.install_confirm_panel);
|
mInstallConfirm = findViewById(R.id.install_confirm_panel);
|
||||||
|
@ -76,10 +76,12 @@ abstract public class Installer {
|
|||||||
public interface InstallerCallback {
|
public interface InstallerCallback {
|
||||||
|
|
||||||
int OPERATION_INSTALL = 1;
|
int OPERATION_INSTALL = 1;
|
||||||
int OPERATION_DELETE = 2;
|
int OPERATION_DELETE = 2;
|
||||||
|
|
||||||
int ERROR_CODE_CANCELED = 1;
|
// Avoid using [-1,1] as they may conflict with Activity.RESULT_*
|
||||||
int ERROR_CODE_OTHER = 2;
|
int ERROR_CODE_CANCELED = 2;
|
||||||
|
int ERROR_CODE_OTHER = 3;
|
||||||
|
int ERROR_CODE_CANNOT_PARSE = 4;
|
||||||
|
|
||||||
void onSuccess(int operation);
|
void onSuccess(int operation);
|
||||||
|
|
||||||
|
@ -140,7 +140,13 @@ public class SystemInstaller extends Installer {
|
|||||||
@Override
|
@Override
|
||||||
protected void installPackageInternal(File apkFile) throws AndroidNotCompatibleException {
|
protected void installPackageInternal(File apkFile) throws AndroidNotCompatibleException {
|
||||||
Uri packageUri = Uri.fromFile(apkFile);
|
Uri packageUri = Uri.fromFile(apkFile);
|
||||||
if (hasNewPermissions(packageUri)) {
|
int count = newPermissionCount(packageUri);
|
||||||
|
if (count < 0) {
|
||||||
|
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||||
|
InstallerCallback.ERROR_CODE_CANNOT_PARSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
Intent intent = new Intent(mContext, InstallConfirmActivity.class);
|
Intent intent = new Intent(mContext, InstallConfirmActivity.class);
|
||||||
intent.setData(packageUri);
|
intent.setData(packageUri);
|
||||||
mActivity.startActivityForResult(intent, REQUEST_CONFIRM_PERMS);
|
mActivity.startActivityForResult(intent, REQUEST_CONFIRM_PERMS);
|
||||||
@ -154,17 +160,20 @@ public class SystemInstaller extends Installer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasNewPermissions(Uri packageUri) {
|
private int newPermissionCount(Uri packageUri) {
|
||||||
AppDiff appDiff = new AppDiff(mContext.getPackageManager(), packageUri);
|
AppDiff appDiff = new AppDiff(mContext.getPackageManager(), packageUri);
|
||||||
if (appDiff.mPkgInfo != null) {
|
if (appDiff.mPkgInfo == null) {
|
||||||
AppSecurityPermissions perms = new AppSecurityPermissions(mContext, appDiff.mPkgInfo);
|
// could not get diff because we couldn't parse the package
|
||||||
if (appDiff.mInstalledAppInfo != null) { // it is an update to an existing app
|
return -1;
|
||||||
// return false if there are no new permissions
|
|
||||||
return (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// default: show install confirm activity
|
AppSecurityPermissions perms = new AppSecurityPermissions(mContext, appDiff.mPkgInfo);
|
||||||
return true;
|
if (appDiff.mInstalledAppInfo != null) {
|
||||||
|
// update to an existing app
|
||||||
|
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
|
||||||
|
}
|
||||||
|
// default: even if there aren't any permissions, we want to make the
|
||||||
|
// user always confirm installing new apps
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doInstallPackageInternal(Uri packageURI) throws AndroidNotCompatibleException {
|
private void doInstallPackageInternal(Uri packageURI) throws AndroidNotCompatibleException {
|
||||||
@ -260,7 +269,10 @@ public class SystemInstaller extends Installer {
|
|||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||||
InstallerCallback.ERROR_CODE_OTHER);
|
InstallerCallback.ERROR_CODE_OTHER);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) {
|
||||||
|
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||||
|
InstallerCallback.ERROR_CODE_CANNOT_PARSE);
|
||||||
|
} else { // Activity.RESULT_CANCELED
|
||||||
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
mCallback.onError(InstallerCallback.OPERATION_INSTALL,
|
||||||
InstallerCallback.ERROR_CODE_CANCELED);
|
InstallerCallback.ERROR_CODE_CANCELED);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user