blob: cacd1051d284c40cc4d6180de1c53a3310607a7a [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.android.packageinstaller;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.VerificationParams;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Select which activity is the first visible activity of the installation and forward the intent to
* it.
*/
public class InstallStart extends Activity {
private static final String LOG_TAG = InstallStart.class.getSimpleName();
private static final String SCHEME_CONTENT = "content";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String callingPackage = getCallingPackage();
// If the activity was started via a PackageInstaller session, we retrieve the calling
// package from that session
int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
if (sessionId != -1) {
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
callingPackage = packageInstaller.getSessionInfo(sessionId).getInstallerPackageName();
}
ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
int originatingUid = getOriginatingUid(sourceInfo);
Intent nextActivity = new Intent(intent);
nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
nextActivity.putExtras(intent.getExtras());
// The the installation source as the nextActivity thinks this activity is the source, hence
// set the originating UID and sourceInfo explicitly
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
Uri packageUri = intent.getData();
if (packageUri == null) {
// if there's nothing to do, quietly slip into the ether
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT,
PackageManager.INSTALL_FAILED_INVALID_URI);
setResult(RESULT_FIRST_USER, result);
nextActivity = null;
} else {
if (packageUri.getScheme().equals(SCHEME_CONTENT)) {
nextActivity.setClass(this, InstallStaging.class);
} else {
nextActivity.setClass(this, PackageInstallerActivity.class);
}
}
}
if (nextActivity != null) {
startActivity(nextActivity);
}
finish();
}
/**
* @return the ApplicationInfo for the installation source (the calling package), if available
*/
private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
if (callingPackage != null) {
try {
return getPackageManager().getApplicationInfo(callingPackage, 0);
} catch (PackageManager.NameNotFoundException ex) {
// ignore
}
}
return null;
}
/**
* Get the originating uid if possible, or VerificationParams.NO_UID if not available
*
* @param sourceInfo The source of this installation
*
* @return The UID of the installation source or VerificationParams.NO_UID
*/
private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
// The originating uid from the intent. We only trust/use this if it comes from a
// system application
int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
VerificationParams.NO_UID);
// Get the source info from the calling package, if available. This will be the
// definitive calling package, but it only works if the intent was started using
// startActivityForResult,
if (sourceInfo != null) {
if (uidFromIntent != VerificationParams.NO_UID &&
(sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
return uidFromIntent;
}
// We either didn't get a uid in the intent, or we don't trust it. Use the
// uid of the calling package instead.
return sourceInfo.uid;
}
// We couldn't get the specific calling package. Let's get the uid instead
int callingUid;
try {
callingUid = ActivityManager.getService()
.getLaunchedFromUid(getActivityToken());
} catch (android.os.RemoteException ex) {
Log.w(LOG_TAG, "Could not determine the launching uid.");
// nothing else we can do
return VerificationParams.NO_UID;
}
// If we got a uid from the intent, we need to verify that the caller is a
// privileged system package before we use it
if (uidFromIntent != VerificationParams.NO_UID) {
String[] callingPackages = getPackageManager().getPackagesForUid(callingUid);
if (callingPackages != null) {
for (String packageName: callingPackages) {
try {
ApplicationInfo applicationInfo =
getPackageManager().getApplicationInfo(packageName, 0);
if ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
!= 0) {
return uidFromIntent;
}
} catch (PackageManager.NameNotFoundException ex) {
// ignore it, and try the next package
}
}
}
}
// We either didn't get a uid from the intent, or we don't trust it. Use the
// calling uid instead.
return callingUid;
}
}