Security model for moving sharesheet to systemui
ResolverActivity (still in frameworks) now requests a "permission token" that it hands to a stubbed system ui activity ChooserActivity. This permission token allows an app (SysUI) with the signed permission "START_ACTIVITY_AS_CALLER" to call ActivityManagerService#startActivityAsCaller. Permission tokens are a one-time use, limited-time offer. Test: runtest systemui && manual testing Bug: 69850752 Change-Id: I3600e1a8ff9eea7397f5f59853423c79b6401f98
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package android.app;
|
||||
|
||||
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
|
||||
|
||||
import static java.lang.Character.MIN_VALUE;
|
||||
|
||||
import android.annotation.CallSuper;
|
||||
@@ -4671,6 +4672,7 @@ public class Activity extends ContextThemeWrapper
|
||||
* their launch had come from the original activity.
|
||||
* @param intent The Intent to start.
|
||||
* @param options ActivityOptions or null.
|
||||
* @param permissionToken Token received from the system that permits this call to be made.
|
||||
* @param ignoreTargetSecurity If true, the activity manager will not check whether the
|
||||
* caller it is doing the start is, is actually allowed to start the target activity.
|
||||
* If you set this to true, you must set an explicit component in the Intent and do any
|
||||
@@ -4679,7 +4681,7 @@ public class Activity extends ContextThemeWrapper
|
||||
* @hide
|
||||
*/
|
||||
public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
|
||||
boolean ignoreTargetSecurity, int userId) {
|
||||
IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
|
||||
if (mParent != null) {
|
||||
throw new RuntimeException("Can't be called from a child");
|
||||
}
|
||||
@@ -4687,7 +4689,7 @@ public class Activity extends ContextThemeWrapper
|
||||
Instrumentation.ActivityResult ar =
|
||||
mInstrumentation.execStartActivityAsCaller(
|
||||
this, mMainThread.getApplicationThread(), mToken, this,
|
||||
intent, -1, options, ignoreTargetSecurity, userId);
|
||||
intent, -1, options, permissionToken, ignoreTargetSecurity, userId);
|
||||
if (ar != null) {
|
||||
mMainThread.sendActivityResult(
|
||||
mToken, mEmbeddedID, -1, ar.getResultCode(),
|
||||
|
||||
@@ -443,6 +443,31 @@ public class ActivityManager {
|
||||
*/
|
||||
public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5;
|
||||
|
||||
/**
|
||||
* Extra included on intents that are delegating the call to
|
||||
* ActivityManager#startActivityAsCaller to another app. This token is necessary for that call
|
||||
* to succeed. Type is IBinder.
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_PERMISSION_TOKEN = "android.app.extra.PERMISSION_TOKEN";
|
||||
|
||||
/**
|
||||
* Extra included on intents that contain an EXTRA_INTENT, with options that the contained
|
||||
* intent may want to be started with. Type is Bundle.
|
||||
* TODO: remove once the ChooserActivity moves to systemui
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_OPTIONS = "android.app.extra.OPTIONS";
|
||||
|
||||
/**
|
||||
* Extra included on intents that contain an EXTRA_INTENT, use this boolean value for the
|
||||
* parameter of the same name when starting the contained intent.
|
||||
* TODO: remove once the ChooserActivity moves to systemui
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_IGNORE_TARGET_SECURITY =
|
||||
"android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
|
||||
|
||||
/** @hide User operation call: success! */
|
||||
public static final int USER_OP_SUCCESS = 0;
|
||||
|
||||
|
||||
@@ -438,10 +438,11 @@ interface IActivityManager {
|
||||
boolean isTopOfTask(in IBinder token);
|
||||
void notifyLaunchTaskBehindComplete(in IBinder token);
|
||||
void notifyEnterAnimationComplete(in IBinder token);
|
||||
IBinder requestStartActivityPermissionToken(in IBinder delegatorToken);
|
||||
int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
|
||||
in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
|
||||
int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
|
||||
boolean ignoreTargetSecurity, int userId);
|
||||
in IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
|
||||
int addAppTask(in IBinder activityToken, in Intent intent,
|
||||
in ActivityManager.TaskDescription description, in Bitmap thumbnail);
|
||||
Point getAppTaskThumbnailSize();
|
||||
|
||||
@@ -1872,8 +1872,8 @@ public class Instrumentation {
|
||||
*/
|
||||
public ActivityResult execStartActivityAsCaller(
|
||||
Context who, IBinder contextThread, IBinder token, Activity target,
|
||||
Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity,
|
||||
int userId) {
|
||||
Intent intent, int requestCode, Bundle options, IBinder permissionToken,
|
||||
boolean ignoreTargetSecurity, int userId) {
|
||||
IApplicationThread whoThread = (IApplicationThread) contextThread;
|
||||
if (mActivityMonitors != null) {
|
||||
synchronized (mSync) {
|
||||
@@ -1904,7 +1904,8 @@ public class Instrumentation {
|
||||
.startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
|
||||
intent.resolveTypeIfNeeded(who.getContentResolver()),
|
||||
token, target != null ? target.mEmbeddedID : null,
|
||||
requestCode, 0, null, options, ignoreTargetSecurity, userId);
|
||||
requestCode, 0, null, options, permissionToken,
|
||||
ignoreTargetSecurity, userId);
|
||||
checkStartActivityResult(result, intent);
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException("Failure from system", e);
|
||||
|
||||
@@ -841,7 +841,7 @@ public class ChooserActivity extends ResolverActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startAsCaller(Activity activity, Bundle options, int userId) {
|
||||
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
|
||||
final Intent intent = getBaseIntentToSend();
|
||||
if (intent == null) {
|
||||
return false;
|
||||
@@ -860,8 +860,7 @@ public class ChooserActivity extends ResolverActivity {
|
||||
final boolean ignoreTargetSecurity = mSourceInfo != null
|
||||
&& mSourceInfo.getResolvedComponentName().getPackageName()
|
||||
.equals(mChooserTarget.getComponentName().getPackageName());
|
||||
activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId);
|
||||
return true;
|
||||
return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -107,7 +107,7 @@ public class IntentForwarderActivity extends Activity {
|
||||
|| ChooserActivity.class.getName().equals(ri.activityInfo.name));
|
||||
|
||||
try {
|
||||
startActivityAsCaller(newIntent, null, false, targetUserId);
|
||||
startActivityAsCaller(newIntent, null, null, false, targetUserId);
|
||||
} catch (RuntimeException e) {
|
||||
int launchedFromUid = -1;
|
||||
String launchedFromPackage = "?";
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.PatternMatcher;
|
||||
import android.os.RemoteException;
|
||||
import android.os.StrictMode;
|
||||
@@ -857,6 +858,36 @@ public class ResolverActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
|
||||
int userId) {
|
||||
// Pass intent to delegate chooser activity with permission token.
|
||||
// TODO: This should move to a trampoline Activity in the system when the ChooserActivity
|
||||
// moves into systemui
|
||||
try {
|
||||
// TODO: Once this is a small springboard activity, it can move off the UI process
|
||||
// and we can move the request method to ActivityManagerInternal.
|
||||
IBinder permissionToken = ActivityManager.getService()
|
||||
.requestStartActivityPermissionToken(getActivityToken());
|
||||
final Intent chooserIntent = new Intent();
|
||||
final ComponentName delegateActivity = ComponentName.unflattenFromString(
|
||||
Resources.getSystem().getString(R.string.config_chooserActivity));
|
||||
chooserIntent.setClassName(delegateActivity.getPackageName(),
|
||||
delegateActivity.getClassName());
|
||||
chooserIntent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, permissionToken);
|
||||
|
||||
// TODO: These extras will change as chooser activity moves into systemui
|
||||
chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
|
||||
chooserIntent.putExtra(ActivityManager.EXTRA_OPTIONS, options);
|
||||
chooserIntent.putExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY,
|
||||
ignoreTargetSecurity);
|
||||
chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
|
||||
startActivity(chooserIntent);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onActivityStarted(TargetInfo cti) {
|
||||
// Do nothing
|
||||
}
|
||||
@@ -1181,9 +1212,8 @@ public class ResolverActivity extends Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startAsCaller(Activity activity, Bundle options, int userId) {
|
||||
activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
|
||||
return true;
|
||||
public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
|
||||
return activity.startAsCallerImpl(mResolvedIntent, options, false, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1242,7 +1272,7 @@ public class ResolverActivity extends Activity {
|
||||
* @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
|
||||
* @return true if the start completed successfully
|
||||
*/
|
||||
boolean startAsCaller(Activity activity, Bundle options, int userId);
|
||||
boolean startAsCaller(ResolverActivity activity, Bundle options, int userId);
|
||||
|
||||
/**
|
||||
* Start the activity referenced by this target as a given user.
|
||||
|
||||
@@ -1929,6 +1929,12 @@
|
||||
<permission android:name="android.permission.START_ANY_ACTIVITY"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- Allows an application to start an activity as another app, provided that app has been
|
||||
granted a permissionToken from the ActivityManagerService.
|
||||
@hide -->
|
||||
<permission android:name="android.permission.START_ACTIVITY_AS_CALLER"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
|
||||
API is no longer supported. -->
|
||||
<permission android:name="android.permission.RESTART_PACKAGES"
|
||||
|
||||
@@ -2295,7 +2295,10 @@
|
||||
Can be customized for other product types -->
|
||||
<string name="config_chooseTypeAndAccountActivity" translatable="false"
|
||||
>android/android.accounts.ChooseTypeAndAccountActivity</string>
|
||||
|
||||
<!-- Name of the activity that will handle requests to the system to choose an activity for
|
||||
the purposes of resolving an intent. -->
|
||||
<string name="config_chooserActivity" translatable="false"
|
||||
>com.android.systemui/com.android.systemui.chooser.ChooserActivity</string>
|
||||
<!-- Component name of a custom ResolverActivity (Intent resolver) to be used instead of
|
||||
the default framework version. If left empty, then the framework version will be used.
|
||||
Example: com.google.android.myapp/.resolver.MyResolverActivity -->
|
||||
|
||||
@@ -1071,6 +1071,7 @@
|
||||
<java-symbol type="string" name="owner_name" />
|
||||
<java-symbol type="string" name="config_chooseAccountActivity" />
|
||||
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
|
||||
<java-symbol type="string" name="config_chooserActivity" />
|
||||
<java-symbol type="string" name="config_customResolverActivity" />
|
||||
<java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
|
||||
<java-symbol type="string" name="error_message_title" />
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
@@ -269,8 +270,8 @@ public class IntentForwarderActivityTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityAsCaller(Intent intent, @Nullable Bundle options, boolean
|
||||
ignoreTargetSecurity, int userId) {
|
||||
public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
|
||||
IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
|
||||
mStartActivityIntent = intent;
|
||||
mUserIdActivityLaunchedIn = userId;
|
||||
}
|
||||
@@ -293,4 +294,4 @@ public class IntentForwarderActivityTest {
|
||||
return mPm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,6 +368,7 @@ applications that come with the platform
|
||||
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
|
||||
<permission name="android.permission.REAL_GET_TASKS"/>
|
||||
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
|
||||
<permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
|
||||
<permission name="android.permission.START_TASKS_FROM_RECENTS"/>
|
||||
<permission name="android.permission.STATUS_BAR"/>
|
||||
<permission name="android.permission.STOP_APP_SWITCHES"/>
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
|
||||
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
|
||||
<uses-permission android:name="android.permission.START_ACTIVITY_AS_CALLER" />
|
||||
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
|
||||
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
|
||||
|
||||
@@ -555,6 +556,22 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".chooser.ChooserActivity"
|
||||
android:theme="@*android:style/Theme.NoDisplay"
|
||||
android:finishOnCloseSystemDialogs="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:documentLaunchMode="never"
|
||||
android:relinquishTaskIdentity="true"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
|
||||
android:process=":ui"
|
||||
android:visibleToInstantApps="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.CHOOSER_UI" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.VOICE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Doze with notifications, run in main sysui process for every user -->
|
||||
<service
|
||||
android:name=".doze.DozeService"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.systemui.chooser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
import java.lang.Thread;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class ChooserActivity extends Activity {
|
||||
|
||||
private static final String TAG = "ChooserActivity";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ChooserHelper.onChoose(this);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.systemui.chooser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
public class ChooserHelper {
|
||||
|
||||
private static final String TAG = "ChooserHelper";
|
||||
|
||||
static void onChoose(Activity activity) {
|
||||
final Intent thisIntent = activity.getIntent();
|
||||
final Bundle thisExtras = thisIntent.getExtras();
|
||||
final Intent chosenIntent = thisIntent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
final Bundle options = thisIntent.getParcelableExtra(ActivityManager.EXTRA_OPTIONS);
|
||||
final IBinder permissionToken =
|
||||
thisExtras.getBinder(ActivityManager.EXTRA_PERMISSION_TOKEN);
|
||||
final boolean ignoreTargetSecurity =
|
||||
thisIntent.getBooleanExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY, false);
|
||||
final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1);
|
||||
activity.startActivityAsCaller(
|
||||
chosenIntent, options, permissionToken, ignoreTargetSecurity, userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.systemui.chooser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.systemui.chooser.ChooserHelper;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyBoolean;
|
||||
import static org.mockito.Mockito.anyFloat;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ChooserHelperTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnChoose_CallsStartActivityAsCallerWithToken() {
|
||||
final Intent intent = new Intent();
|
||||
final Binder token = new Binder();
|
||||
intent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, token);
|
||||
|
||||
final Activity mockActivity = mock(Activity.class);
|
||||
when(mockActivity.getIntent()).thenReturn(intent);
|
||||
|
||||
ChooserHelper.onChoose(mockActivity);
|
||||
verify(mockActivity, times(1)).startActivityAsCaller(
|
||||
any(), any(), eq(token), anyBoolean(), anyInt());
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
|
||||
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
|
||||
import static android.Manifest.permission.READ_FRAME_BUFFER;
|
||||
import static android.Manifest.permission.REMOVE_TASKS;
|
||||
import static android.Manifest.permission.START_ACTIVITY_AS_CALLER;
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
|
||||
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
|
||||
@@ -554,6 +555,23 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
// could take much longer than usual.
|
||||
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
|
||||
|
||||
// Permission tokens are used to temporarily granted a trusted app the ability to call
|
||||
// #startActivityAsCaller. A client is expected to dump its token after this time has elapsed,
|
||||
// showing any appropriate error messages to the user.
|
||||
private static final long START_AS_CALLER_TOKEN_TIMEOUT =
|
||||
10 * DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
// How long before the service actually expires a token. This is slightly longer than
|
||||
// START_AS_CALLER_TOKEN_TIMEOUT, to provide a buffer so clients will rarely encounter the
|
||||
// expiration exception.
|
||||
private static final long START_AS_CALLER_TOKEN_TIMEOUT_IMPL =
|
||||
START_AS_CALLER_TOKEN_TIMEOUT + 2*1000;
|
||||
|
||||
// How long the service will remember expired tokens, for the purpose of providing error
|
||||
// messaging when a client uses an expired token.
|
||||
private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT =
|
||||
START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * DateUtils.MINUTE_IN_MILLIS;
|
||||
|
||||
// How long we allow a receiver to run before giving up on it.
|
||||
static final int BROADCAST_FG_TIMEOUT = 10*1000;
|
||||
static final int BROADCAST_BG_TIMEOUT = 60*1000;
|
||||
@@ -662,6 +680,13 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
|
||||
final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
|
||||
|
||||
// Activity tokens of system activities that are delegating their call to
|
||||
// #startActivityByCaller, keyed by the permissionToken granted to the delegate.
|
||||
final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>();
|
||||
|
||||
// Permission tokens that have expired, but we remember for error reporting.
|
||||
final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>();
|
||||
|
||||
public final IntentFirewall mIntentFirewall;
|
||||
|
||||
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
|
||||
@@ -1781,6 +1806,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
|
||||
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
|
||||
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
|
||||
static final int EXPIRE_START_AS_CALLER_TOKEN_MSG = 75;
|
||||
static final int FORGET_START_AS_CALLER_TOKEN_MSG = 76;
|
||||
|
||||
static final int FIRST_ACTIVITY_STACK_MSG = 100;
|
||||
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
|
||||
@@ -2445,6 +2472,19 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case EXPIRE_START_AS_CALLER_TOKEN_MSG: {
|
||||
synchronized (ActivityManagerService.this) {
|
||||
final IBinder permissionToken = (IBinder)msg.obj;
|
||||
mStartActivitySources.remove(permissionToken);
|
||||
mExpiredStartAsCallerTokens.add(permissionToken);
|
||||
}
|
||||
} break;
|
||||
case FORGET_START_AS_CALLER_TOKEN_MSG: {
|
||||
synchronized (ActivityManagerService.this) {
|
||||
final IBinder permissionToken = (IBinder)msg.obj;
|
||||
mExpiredStartAsCallerTokens.remove(permissionToken);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -4712,16 +4752,54 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Only callable from the system. This token grants a temporary permission to call
|
||||
* #startActivityAsCallerWithToken. The token will time out after
|
||||
* START_AS_CALLER_TOKEN_TIMEOUT if it is not used.
|
||||
*
|
||||
* @param delegatorToken The Binder token referencing the system Activity that wants to delegate
|
||||
* the #startActivityAsCaller to another app. The "caller" will be the caller of this
|
||||
* activity's token, not the delegate's caller (which is probably the delegator itself).
|
||||
*
|
||||
* @return Returns a token that can be given to a "delegate" app that may call
|
||||
* #startActivityAsCaller
|
||||
*/
|
||||
@Override
|
||||
public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
|
||||
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
|
||||
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
|
||||
int userId) {
|
||||
public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
|
||||
throw new SecurityException("Only the system process can request a permission token, " +
|
||||
"received request from uid: " + callingUid);
|
||||
}
|
||||
IBinder permissionToken = new Binder();
|
||||
synchronized (this) {
|
||||
mStartActivitySources.put(permissionToken, delegatorToken);
|
||||
}
|
||||
|
||||
Message expireMsg = mHandler.obtainMessage(EXPIRE_START_AS_CALLER_TOKEN_MSG,
|
||||
permissionToken);
|
||||
mHandler.sendMessageDelayed(expireMsg, START_AS_CALLER_TOKEN_TIMEOUT_IMPL);
|
||||
|
||||
Message forgetMsg = mHandler.obtainMessage(FORGET_START_AS_CALLER_TOKEN_MSG,
|
||||
permissionToken);
|
||||
mHandler.sendMessageDelayed(forgetMsg, START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT);
|
||||
|
||||
return permissionToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int startActivityAsCaller(IApplicationThread caller,
|
||||
String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
|
||||
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
|
||||
Bundle bOptions, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
|
||||
// This is very dangerous -- it allows you to perform a start activity (including
|
||||
// permission grants) as any app that may launch one of your own activities. So
|
||||
// we will only allow this to be done from activities that are part of the core framework,
|
||||
// and then only when they are running as the system.
|
||||
// permission grants) as any app that may launch one of your own activities. So we only
|
||||
// allow this in two cases:
|
||||
// 1) The caller is an activity that is part of the core framework, and then only when it
|
||||
// is running as the system.
|
||||
// 2) The caller provides a valid permissionToken. Permission tokens are one-time use and
|
||||
// can only be requested by a system activity, which may then delegate this call to
|
||||
// another app.
|
||||
final ActivityRecord sourceRecord;
|
||||
final int targetUid;
|
||||
final String targetPackage;
|
||||
@@ -4729,17 +4807,47 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
if (resultTo == null) {
|
||||
throw new SecurityException("Must be called from an activity");
|
||||
}
|
||||
sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
|
||||
if (sourceRecord == null) {
|
||||
throw new SecurityException("Called with bad activity token: " + resultTo);
|
||||
|
||||
final IBinder sourceToken;
|
||||
if (permissionToken != null) {
|
||||
// To even attempt to use a permissionToken, an app must also have this signature
|
||||
// permission.
|
||||
enforceCallingPermission(android.Manifest.permission.START_ACTIVITY_AS_CALLER,
|
||||
"startActivityAsCaller");
|
||||
// If called with a permissionToken, we want the sourceRecord from the delegator
|
||||
// activity that requested this token.
|
||||
sourceToken =
|
||||
mStartActivitySources.remove(permissionToken);
|
||||
if (sourceToken == null) {
|
||||
// Invalid permissionToken, check if it recently expired.
|
||||
if (mExpiredStartAsCallerTokens.contains(permissionToken)) {
|
||||
throw new SecurityException("Called with expired permission token: "
|
||||
+ permissionToken);
|
||||
} else {
|
||||
throw new SecurityException("Called with invalid permission token: "
|
||||
+ permissionToken);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This method was called directly by the source.
|
||||
sourceToken = resultTo;
|
||||
}
|
||||
if (!sourceRecord.info.packageName.equals("android")) {
|
||||
throw new SecurityException(
|
||||
"Must be called from an activity that is declared in the android package");
|
||||
|
||||
sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
|
||||
if (sourceRecord == null) {
|
||||
throw new SecurityException("Called with bad activity token: " + sourceToken);
|
||||
}
|
||||
if (sourceRecord.app == null) {
|
||||
throw new SecurityException("Called without a process attached to activity");
|
||||
}
|
||||
|
||||
// Whether called directly or from a delegate, the source activity must be from the
|
||||
// android package.
|
||||
if (!sourceRecord.info.packageName.equals("android")) {
|
||||
throw new SecurityException("Must be called from an activity that is " +
|
||||
"declared in the android package");
|
||||
}
|
||||
|
||||
if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
|
||||
// This is still okay, as long as this activity is running under the
|
||||
// uid of the original calling activity.
|
||||
@@ -4750,6 +4858,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
+ sourceRecord.launchedFromUid);
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreTargetSecurity) {
|
||||
if (intent.getComponent() == null) {
|
||||
throw new SecurityException(
|
||||
|
||||
Reference in New Issue
Block a user