Skip to content

Commit fb81d09

Browse files
author
Dianne Hackborn
committed
Fix issue #22860466: viapi security bug - rubber stamping in nested VIs
Add new Activity.isVoiceInteractionRoot() API that an activity can use to determine whether it is the root activity of a voice interaction session started by the user's designated voice interaction service. This is a special new API that apps must explicitly check, because as with visual activities the model behind an activity should usually be that it accomplishes its task by interacting with the user (implicitly getting their approval) rather than trusting that whoever invoked it is telling it to do what the user once. In the voice world, however, there are some cases where quick interactions want to allow for immediate execution without further user involvement, so this API allows for that without opening up security holes from other applications. Change-Id: Ie02d2458f16cb0b12af825641bcf8beaf086931b
1 parent 0ca1e98 commit fb81d09

11 files changed

Lines changed: 92 additions & 7 deletions

File tree

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3341,6 +3341,7 @@ package android.app {
33413341
method public boolean isImmersive();
33423342
method public boolean isTaskRoot();
33433343
method public boolean isVoiceInteraction();
3344+
method public boolean isVoiceInteractionRoot();
33443345
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
33453346
method public boolean moveTaskToBack(boolean);
33463347
method public boolean navigateUpTo(android.content.Intent);

api/system-current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,6 +3444,7 @@ package android.app {
34443444
method public boolean isImmersive();
34453445
method public boolean isTaskRoot();
34463446
method public boolean isVoiceInteraction();
3447+
method public boolean isVoiceInteractionRoot();
34473448
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
34483449
method public boolean moveTaskToBack(boolean);
34493450
method public boolean navigateUpTo(android.content.Intent);

core/java/android/app/Activity.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,22 @@ public boolean isVoiceInteraction() {
12281228
return mVoiceInteractor != null;
12291229
}
12301230

1231+
/**
1232+
* Like {@link #isVoiceInteraction}, but only returns true if this is also the root
1233+
* of a voice interaction. That is, returns true if this activity was directly
1234+
* started by the voice interaction service as the initiation of a voice interaction.
1235+
* Otherwise, for example if it was started by another activity while under voice
1236+
* interaction, returns false.
1237+
*/
1238+
public boolean isVoiceInteractionRoot() {
1239+
try {
1240+
return mVoiceInteractor != null
1241+
&& ActivityManagerNative.getDefault().isRootVoiceInteraction(mToken);
1242+
} catch (RemoteException e) {
1243+
}
1244+
return false;
1245+
}
1246+
12311247
/**
12321248
* Retrieve the active {@link VoiceInteractor} that the user is going through to
12331249
* interact with this activity.

core/java/android/app/ActivityManagerNative.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2582,6 +2582,15 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
25822582
reply.writeInt(res ? 1 : 0);
25832583
return true;
25842584
}
2585+
2586+
case IS_ROOT_VOICE_INTERACTION_TRANSACTION: {
2587+
data.enforceInterface(IActivityManager.descriptor);
2588+
IBinder token = data.readStrongBinder();
2589+
boolean res = isRootVoiceInteraction(token);
2590+
reply.writeNoException();
2591+
reply.writeInt(res ? 1 : 0);
2592+
return true;
2593+
}
25852594
}
25862595

25872596
return super.onTransact(code, data, reply, flags);
@@ -5962,5 +5971,19 @@ public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
59625971
return res != 0;
59635972
}
59645973

5974+
@Override
5975+
public boolean isRootVoiceInteraction(IBinder token) throws RemoteException {
5976+
Parcel data = Parcel.obtain();
5977+
Parcel reply = Parcel.obtain();
5978+
data.writeInterfaceToken(IActivityManager.descriptor);
5979+
data.writeStrongBinder(token);
5980+
mRemote.transact(IS_ROOT_VOICE_INTERACTION_TRANSACTION, data, reply, 0);
5981+
reply.readException();
5982+
int res = reply.readInt();
5983+
data.recycle();
5984+
reply.recycle();
5985+
return res != 0;
5986+
}
5987+
59655988
private IBinder mRemote;
59665989
}

core/java/android/app/IActivityManager.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,8 @@ public int getPackageProcessState(String packageName, String callingPackage)
515515
public boolean setProcessMemoryTrimLevel(String process, int uid, int level)
516516
throws RemoteException;
517517

518+
public boolean isRootVoiceInteraction(IBinder token) throws RemoteException;
519+
518520
/*
519521
* Private non-Binder interfaces
520522
*/
@@ -861,4 +863,5 @@ private WaitResult(Parcel source) {
861863
int IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION
862864
= IBinder.FIRST_CALL_TRANSACTION+299;
863865
int SHOW_ASSIST_FROM_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+300;
866+
int IS_ROOT_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+301;
864867
}

services/core/java/com/android/server/am/ActivityManagerService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6551,6 +6551,17 @@ public String getPackageForToken(IBinder token) {
65516551
}
65526552
}
65536553

6554+
@Override
6555+
public boolean isRootVoiceInteraction(IBinder token) {
6556+
synchronized(this) {
6557+
ActivityRecord r = ActivityRecord.isInStackLocked(token);
6558+
if (r == null) {
6559+
return false;
6560+
}
6561+
return r.rootVoiceInteraction;
6562+
}
6563+
}
6564+
65546565
@Override
65556566
public IIntentSender getIntentSender(int type,
65566567
String packageName, IBinder token, String resultWho,

services/core/java/com/android/server/am/ActivityRecord.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ final class ActivityRecord {
107107
boolean fullscreen; // covers the full screen?
108108
final boolean noDisplay; // activity is not displayed?
109109
final boolean componentSpecified; // did caller specifiy an explicit component?
110+
final boolean rootVoiceInteraction; // was this the root activity of a voice interaction?
110111

111112
static final int APPLICATION_ACTIVITY_TYPE = 0;
112113
static final int HOME_ACTIVITY_TYPE = 1;
@@ -207,6 +208,9 @@ void dump(PrintWriter pw, String prefix) {
207208
pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
208209
pw.print(" componentSpecified="); pw.print(componentSpecified);
209210
pw.print(" mActivityType="); pw.println(mActivityType);
211+
if (rootVoiceInteraction) {
212+
pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction);
213+
}
210214
pw.print(prefix); pw.print("compat="); pw.print(compat);
211215
pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes));
212216
pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
@@ -432,7 +436,8 @@ boolean isNotResolverActivity() {
432436
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
433437
ActivityInfo aInfo, Configuration _configuration,
434438
ActivityRecord _resultTo, String _resultWho, int _reqCode,
435-
boolean _componentSpecified, ActivityStackSupervisor supervisor,
439+
boolean _componentSpecified, boolean _rootVoiceInteraction,
440+
ActivityStackSupervisor supervisor,
436441
ActivityContainer container, Bundle options) {
437442
service = _service;
438443
appToken = new Token(this, service);
@@ -444,6 +449,7 @@ boolean isNotResolverActivity() {
444449
shortComponentName = _intent.getComponent().flattenToShortString();
445450
resolvedType = _resolvedType;
446451
componentSpecified = _componentSpecified;
452+
rootVoiceInteraction = _rootVoiceInteraction;
447453
configuration = _configuration;
448454
stackConfigOverride = (container != null)
449455
? container.mStack.mOverrideConfig : Configuration.EMPTY;
@@ -1257,7 +1263,7 @@ static ActivityRecord restoreFromXml(XmlPullParser in,
12571263
}
12581264
final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid,
12591265
launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(),
1260-
null, null, 0, componentSpecified, stackSupervisor, null, null);
1266+
null, null, 0, componentSpecified, false, stackSupervisor, null, null);
12611267

12621268
r.persistentState = persistentState;
12631269
r.taskDescription = taskDescription;

services/core/java/com/android/server/am/ActivityStackSupervisor.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,7 @@ final int startActivityLocked(IApplicationThread caller,
15061506
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
15071507
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
15081508
try {
1509+
intent.addCategory(Intent.CATEGORY_VOICE);
15091510
if (!AppGlobals.getPackageManager().activitySupportsIntent(
15101511
intent.getComponent(), intent, resolvedType)) {
15111512
err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
@@ -1626,7 +1627,7 @@ final int startActivityLocked(IApplicationThread caller,
16261627

16271628
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
16281629
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
1629-
requestCode, componentSpecified, this, container, options);
1630+
requestCode, componentSpecified, voiceSession != null, this, container, options);
16301631
if (outActivity != null) {
16311632
outActivity[0] = r;
16321633
}
@@ -3721,19 +3722,19 @@ final void scheduleSleepTimeout() {
37213722

37223723
@Override
37233724
public void onDisplayAdded(int displayId) {
3724-
Slog.v(TAG, "Display added displayId=" + displayId);
3725+
if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
37253726
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
37263727
}
37273728

37283729
@Override
37293730
public void onDisplayRemoved(int displayId) {
3730-
Slog.v(TAG, "Display removed displayId=" + displayId);
3731+
if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
37313732
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
37323733
}
37333734

37343735
@Override
37353736
public void onDisplayChanged(int displayId) {
3736-
Slog.v(TAG, "Display changed displayId=" + displayId);
3737+
if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
37373738
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
37383739
}
37393740

tests/VoiceInteraction/res/layout/test_interaction.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@
3434
android:textAppearance="?android:attr/textAppearanceMedium"
3535
/>
3636

37+
<LinearLayout android:layout_width="match_parent"
38+
android:layout_height="wrap_content"
39+
android:layout_marginTop="16dp"
40+
android:orientation="horizontal">
41+
42+
<Button android:id="@+id/airplane"
43+
android:layout_width="wrap_content"
44+
android:layout_height="wrap_content"
45+
android:text="@string/launchAirplane"
46+
/>
47+
48+
</LinearLayout>
49+
3750
<LinearLayout android:layout_width="match_parent"
3851
android:layout_height="wrap_content"
3952
android:layout_marginTop="16dp"

tests/VoiceInteraction/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<string name="tree">Tree</string>
2222
<string name="text">Text</string>
2323
<string name="asyncStructure">(Async structure goes here)</string>
24+
<string name="launchAirplane">Launch airplane mode</string>
2425
<string name="confirm">Confirm</string>
2526
<string name="abort">Abort</string>
2627
<string name="complete">Complete</string>

0 commit comments

Comments
 (0)