summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-11-11 00:28:15 +0000
committerJustin Dunlap <justindunlap@google.com>2022-12-05 14:08:21 -0800
commiteac683523560b7e3d392eca74fa7b53939df3c3c (patch)
tree2cbdae43778cb3ce9d7ca1ec53c012a2d6b70987
parentb02406e67c742f130b1d4f857071cce0e8177554 (diff)
parentb973b5c8190a967cad12428be7ec87464fed8ddf (diff)
downloadbase-eac683523560b7e3d392eca74fa7b53939df3c3c.tar.gz
Merge cherrypicks of [18563202, 19970324, 15982545, 20120242, 20208339, 20211078, 20291140, 20251345, 20223219, 20122955, 20234866, 20219552, 20426455] into security-aosp-qt-release.android-security-10.0.0_r74
Change-Id: Ifcd157219e4cecce5376bae406c5d57ffd96716e
-rw-r--r--core/java/android/app/AutomaticZenRule.java2
-rw-r--r--core/java/android/hardware/usb/UsbDeviceConnection.java71
-rw-r--r--core/java/android/hardware/usb/UsbRequest.java86
-rw-r--r--core/java/android/service/notification/Condition.java38
-rw-r--r--core/tests/coretests/src/android/service/notification/ConditionTest.java101
-rw-r--r--packages/SettingsProvider/Android.bp5
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java91
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java139
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java24
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java60
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java31
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java3
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java7
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java34
17 files changed, 610 insertions, 105 deletions
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 37b336382769..9a92515ee794 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -125,7 +125,7 @@ public final class AutomaticZenRule implements Parcelable {
name = getTrimmedString(source.readString());
}
interruptionFilter = source.readInt();
- conditionId = source.readParcelable(null);
+ conditionId = getTrimmedUri(source.readParcelable(null));
owner = getTrimmedComponentName(source.readParcelable(null));
configurationActivity = getTrimmedComponentName(source.readParcelable(null));
creationTime = source.readLong();
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 71297c187e5c..5c7a1b0aea3b 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -52,6 +52,8 @@ public class UsbDeviceConnection {
private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final Object mLock = new Object();
+
/**
* UsbDevice should only be instantiated by UsbService implementation
* @hide
@@ -62,13 +64,23 @@ public class UsbDeviceConnection {
/* package */ boolean open(String name, ParcelFileDescriptor pfd, @NonNull Context context) {
mContext = context.getApplicationContext();
- boolean wasOpened = native_open(name, pfd.getFileDescriptor());
- if (wasOpened) {
- mCloseGuard.open("close");
+ synchronized (mLock) {
+ boolean wasOpened = native_open(name, pfd.getFileDescriptor());
+
+ if (wasOpened) {
+ mCloseGuard.open("close");
+ }
+
+ return wasOpened;
}
+ }
- return wasOpened;
+ /***
+ * @return If this connection is currently open and usable.
+ */
+ boolean isOpen() {
+ return mNativeContext != 0;
}
/**
@@ -81,15 +93,60 @@ public class UsbDeviceConnection {
}
/**
+ * Cancel a request which relates to this connection.
+ *
+ * @return true if the request was successfully cancelled.
+ */
+ /* package */ boolean cancelRequest(UsbRequest request) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.cancelIfOpen();
+ }
+ }
+
+ /**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer, length);
+ }
+ }
+
+ /**
+ * This is meant to be called by UsbRequest's queue() in order to synchronize on
+ * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
+ synchronized (mLock) {
+ if (!isOpen()) {
+ return false;
+ }
+
+ return request.queueIfConnectionOpen(buffer);
+ }
+ }
+
+ /**
* Releases all system resources related to the device.
* Once the object is closed it cannot be used again.
* The client must call {@link UsbManager#openDevice} again
* to retrieve a new instance to reestablish communication with the device.
*/
public void close() {
- if (mNativeContext != 0) {
- native_close();
- mCloseGuard.close();
+ synchronized (mLock) {
+ if (isOpen()) {
+ native_close();
+ mCloseGuard.close();
+ }
}
}
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 7abf3e9bcc7a..11c9d06cd7fe 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -112,11 +112,13 @@ public class UsbRequest {
* Releases all resources related to this request.
*/
public void close() {
- if (mNativeContext != 0) {
- mEndpoint = null;
- mConnection = null;
- native_close();
- mCloseGuard.close();
+ synchronized (mLock) {
+ if (mNativeContext != 0) {
+ mEndpoint = null;
+ mConnection = null;
+ native_close();
+ mCloseGuard.close();
+ }
}
}
@@ -190,10 +192,32 @@ public class UsbRequest {
*/
@Deprecated
public boolean queue(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer, length);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new NullPointerException("invalid connection");
+ }
+
boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
boolean result;
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+ if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
&& length > MAX_USBFS_BUFFER_SIZE) {
length = MAX_USBFS_BUFFER_SIZE;
}
@@ -242,6 +266,28 @@ public class UsbRequest {
* @return true if the queueing operation succeeded
*/
public boolean queue(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
+ // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+ // the connection being closed while queueing.
+ return connection.queueRequest(this, buffer);
+ }
+
+ /**
+ * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+ * there, to prevent the connection being closed while queueing.
+ */
+ /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null || !connection.isOpen()) {
+ // The expected exception by CTS Verifier - USB Device test
+ throw new IllegalStateException("invalid connection");
+ }
+
// Request need to be initialized
Preconditions.checkState(mNativeContext != 0, "request is not initialized");
@@ -259,7 +305,7 @@ public class UsbRequest {
mIsUsingNewQueue = true;
wasQueued = native_queue(null, 0, 0);
} else {
- if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+ if (connection.getContext().getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.P) {
// Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -362,6 +408,32 @@ public class UsbRequest {
* @return true if cancelling succeeded
*/
public boolean cancel() {
+ UsbDeviceConnection connection = mConnection;
+ if (connection == null) {
+ return false;
+ }
+
+ return connection.cancelRequest(this);
+ }
+
+ /**
+ * Cancels a pending queue operation (for use when the UsbDeviceConnection associated
+ * with this request is synchronized). This ensures we don't have a race where the
+ * device is closed and then the request is canceled which would lead to a
+ * use-after-free because the cancel operation uses the device connection
+ * information freed in the when UsbDeviceConnection is closed.<br/>
+ *
+ * This method assumes the connected is not closed while this method is executed.
+ *
+ * @return true if cancelling succeeded.
+ */
+ /* package */ boolean cancelIfOpen() {
+ UsbDeviceConnection connection = mConnection;
+ if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
+ Log.w(TAG,
+ "Detected attempt to cancel a request on a connection which isn't open");
+ return false;
+ }
return native_cancel();
}
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index e506509bb1be..50597ec3972e 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -90,6 +90,12 @@ public final class Condition implements Parcelable {
public final int icon;
/**
+ * The maximum string length for any string contained in this condition.
+ * @hide
+ */
+ public static final int MAX_STRING_LENGTH = 1000;
+
+ /**
* An object representing the current state of a {@link android.app.AutomaticZenRule}.
* @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
* @param summary a user visible description of the rule state.
@@ -103,16 +109,19 @@ public final class Condition implements Parcelable {
if (id == null) throw new IllegalArgumentException("id is required");
if (summary == null) throw new IllegalArgumentException("summary is required");
if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
- this.id = id;
- this.summary = summary;
- this.line1 = line1;
- this.line2 = line2;
+ this.id = getTrimmedUri(id);
+ this.summary = getTrimmedString(summary);
+ this.line1 = getTrimmedString(line1);
+ this.line2 = getTrimmedString(line2);
this.icon = icon;
this.state = state;
this.flags = flags;
}
public Condition(Parcel source) {
+ // This constructor passes all fields directly into the constructor that takes all the
+ // fields as arguments; that constructor will trim each of the input strings to
+ // max length if necessary.
this((Uri)source.readParcelable(Condition.class.getClassLoader()),
source.readString(),
source.readString(),
@@ -239,4 +248,25 @@ public final class Condition implements Parcelable {
return new Condition[size];
}
};
+
+ /**
+ * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
+ */
+ private static String getTrimmedString(String input) {
+ if (input != null && input.length() > MAX_STRING_LENGTH) {
+ return input.substring(0, MAX_STRING_LENGTH);
+ }
+ return input;
+ }
+
+ /**
+ * Returns a truncated copy of the Uri by trimming the string representation to the maximum
+ * string length.
+ */
+ private static Uri getTrimmedUri(Uri input) {
+ if (input != null && input.toString().length() > MAX_STRING_LENGTH) {
+ return Uri.parse(getTrimmedString(input.toString()));
+ }
+ return input;
+ }
}
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
new file mode 100644
index 000000000000..42629ba41287
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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 android.service.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.net.Uri;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConditionTest {
+ private static final String CLASS = "android.service.notification.Condition";
+
+ @Test
+ public void testLongFields_inConstructors() {
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+
+ // Confirm strings are truncated via short constructor
+ Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE);
+
+ assertEquals(Condition.MAX_STRING_LENGTH, cond1.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond1.summary.length());
+
+ // Confirm strings are truncated via long constructor
+ Condition cond2 = new Condition(longUri, longString, longString, longString,
+ -1, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_ALWAYS);
+
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.summary.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.line1.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.line2.length());
+ }
+
+ @Test
+ public void testLongFields_viaParcel() {
+ // Set fields via reflection to force them to be long, then parcel and unparcel to make sure
+ // it gets truncated upon unparcelling.
+ Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE);
+
+ try {
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+ Field id = Class.forName(CLASS).getDeclaredField("id");
+ id.setAccessible(true);
+ id.set(cond, longUri);
+ Field summary = Class.forName(CLASS).getDeclaredField("summary");
+ summary.setAccessible(true);
+ summary.set(cond, longString);
+ Field line1 = Class.forName(CLASS).getDeclaredField("line1");
+ line1.setAccessible(true);
+ line1.set(cond, longString);
+ Field line2 = Class.forName(CLASS).getDeclaredField("line2");
+ line2.setAccessible(true);
+ line2.set(cond, longString);
+ } catch (NoSuchFieldException e) {
+ fail(e.toString());
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ } catch (IllegalAccessException e) {
+ fail(e.toString());
+ }
+
+ Parcel parcel = Parcel.obtain();
+ cond.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Condition fromParcel = new Condition(parcel);
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.summary.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length());
+ }
+}
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 1c97fc37cf50..825a0bd98aff 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -24,7 +24,10 @@ android_test {
"src/com/android/providers/settings/SettingsState.java",
"src/com/android/providers/settings/SettingsHelper.java",
],
- static_libs: ["androidx.test.rules"],
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ ],
libs: ["android.test.base"],
resource_dirs: ["res"],
aaptflags: [
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c05c4cdf72d7..451b37391a9d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -48,6 +48,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
@@ -367,9 +368,11 @@ final class SettingsState {
Setting newSetting = new Setting(name, oldSetting.getValue(), null,
oldSetting.getPackageName(), oldSetting.getTag(), false,
oldSetting.getId());
+ int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
+ oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
mSettings.put(name, newSetting);
- updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
- newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
scheduleWriteIfNeededLocked();
}
}
@@ -392,6 +395,13 @@ final class SettingsState {
Setting oldState = mSettings.get(name);
String oldValue = (oldState != null) ? oldState.value : null;
String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
+ String newDefaultValue = makeDefault ? value : oldDefaultValue;
+
+ int newSize = getNewMemoryUsagePerPackageLocked(packageName,
+ oldValue == null ? name.length() : 0 /* deltaKeySize */,
+ oldValue, value, oldDefaultValue, newDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(packageName, newSize);
+
Setting newState;
if (oldState != null) {
@@ -409,8 +419,7 @@ final class SettingsState {
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
- updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
- oldDefaultValue, newState.getDefaultValue());
+ updateMemoryUsagePerPackageLocked(packageName, newSize);
scheduleWriteIfNeededLocked();
@@ -431,13 +440,18 @@ final class SettingsState {
}
Setting oldState = mSettings.remove(name);
+ if (oldState == null) {
+ return false;
+ }
+ int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
+ -name.length() /* deltaKeySize */,
+ oldState.value, null, oldState.defaultValue, null);
StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
StatsLog.SETTING_CHANGED__REASON__DELETED);
- updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
- null, oldState.defaultValue, null);
+ updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
@@ -454,20 +468,23 @@ final class SettingsState {
}
Setting setting = mSettings.get(name);
+ if (setting == null) {
+ return false;
+ }
Setting oldSetting = new Setting(setting);
String oldValue = setting.getValue();
String oldDefaultValue = setting.getDefaultValue();
+ int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
+ oldDefaultValue, oldDefaultValue, oldDefaultValue);
+ checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
+
if (!setting.reset()) {
return false;
}
- String newValue = setting.getValue();
- String newDefaultValue = setting.getDefaultValue();
-
- updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
- newValue, oldDefaultValue, newDefaultValue);
+ updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
@@ -575,38 +592,49 @@ final class SettingsState {
}
@GuardedBy("mLock")
- private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
- String newValue, String oldDefaultValue, String newDefaultValue) {
- if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
- return;
- }
+ private boolean isExemptFromMemoryUsageCap(String packageName) {
+ return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
+ || SYSTEM_PACKAGE_NAME.equals(packageName);
+ }
- if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
+ @GuardedBy("mLock")
+ private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
+ throws IllegalStateException {
+ if (isExemptFromMemoryUsageCap(packageName)) {
return;
}
+ if (newSize > mMaxBytesPerAppPackage) {
+ throw new IllegalStateException("You are adding too many system settings. "
+ + "You should stop using system settings for app specific data"
+ + " package: " + packageName);
+ }
+ }
+ @GuardedBy("mLock")
+ private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize,
+ String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return 0;
+ }
+ final Integer currentSize = mPackageToMemoryUsage.get(packageName);
final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
final int newValueSize = (newValue != null) ? newValue.length() : 0;
final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
- final int deltaSize = newValueSize + newDefaultValueSize
+ final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize
- oldValueSize - oldDefaultValueSize;
+ return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
+ }
- Integer currentSize = mPackageToMemoryUsage.get(packageName);
- final int newSize = Math.max((currentSize != null)
- ? currentSize + deltaSize : deltaSize, 0);
-
- if (newSize > mMaxBytesPerAppPackage) {
- throw new IllegalStateException("You are adding too many system settings. "
- + "You should stop using system settings for app specific data"
- + " package: " + packageName);
+ @GuardedBy("mLock")
+ private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
+ if (isExemptFromMemoryUsageCap(packageName)) {
+ return;
}
-
if (DEBUG) {
Slog.i(LOG_TAG, "Settings for package: " + packageName
+ " size: " + newSize + " bytes.");
}
-
mPackageToMemoryUsage.put(packageName, newSize);
}
@@ -1220,4 +1248,11 @@ final class SettingsState {
return false;
}
}
+
+ @VisibleForTesting
+ public int getMemoryUsage(String packageName) {
+ synchronized (mLock) {
+ return mPackageToMemoryUsage.getOrDefault(packageName, 0);
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 3f68554ffe87..adb356726eec 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -21,6 +21,8 @@ import android.util.Xml;
import org.xmlpull.v1.XmlSerializer;
+import com.google.common.base.Strings;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -46,7 +48,6 @@ public class SettingsStateTest extends AndroidTestCase {
"\uD800ab\uDC00 " + // broken surrogate pairs
"日本語";
-
public void testIsBinary() {
assertFalse(SettingsState.isBinary(" abc 日本語"));
@@ -182,4 +183,140 @@ public class SettingsStateTest extends AndroidTestCase {
assertEquals("p2", s.getPackageName());
}
}
+
+ public void testInsertSetting_memoryUsage() {
+ final Object lock = new Object();
+ final File file = new File(getContext().getCacheDir(), "setting.xml");
+ final String settingName = "test_setting";
+
+ SettingsState settingsState = new SettingsState(getContext(), lock, file, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+ // No exception should be thrown when there is no cap
+ settingsState.insertSettingLocked(settingName, Strings.repeat("A", 20001),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(settingName);
+
+ settingsState = new SettingsState(getContext(), lock, file, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+ // System package doesn't have memory usage limit
+ settingsState.insertSettingLocked(settingName, Strings.repeat("A", 20001),
+ null, false, "android");
+ settingsState.deleteSettingLocked(settingName);
+
+ // Should not throw if usage is under the cap
+ settingsState.insertSettingLocked(settingName, Strings.repeat("A", 19975),
+ null, false, "p1");
+ settingsState.deleteSettingLocked(settingName);
+ try {
+ settingsState.insertSettingLocked(settingName, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ try {
+ settingsState.insertSettingLocked(settingName, Strings.repeat("A", 20001),
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("p1"));
+ }
+ assertTrue(settingsState.getSettingLocked(settingName).isNull());
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
+ null, false, "p1");
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ }
+
+ public void testMemoryUsagePerPackage() {
+ final Object lock = new Object();
+ final File file = new File(getContext().getCacheDir(), "setting.xml");
+ final String testPackage = "package";
+ SettingsState settingsState = new SettingsState(getContext(), lock, file, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+
+ // Test inserting one key with default
+ final String settingName = "test_setting";
+ final String testKey1 = settingName;
+ final String testValue1 = Strings.repeat("A", 100);
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, testPackage);
+ int expectedMemUsage = testKey1.length() + testValue1.length()
+ + testValue1.length() /* size for default */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test inserting another key
+ final String testKey2 = settingName + "2";
+ settingsState.insertSettingLocked(testKey2, testValue1, null, false, testPackage);
+ expectedMemUsage += testKey2.length() + testValue1.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test updating first key with new default
+ final String testValue2 = Strings.repeat("A", 300);
+ settingsState.insertSettingLocked(testKey1, testValue2, null, true, testPackage);
+ expectedMemUsage += (testValue2.length() - testValue1.length()) * 2;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test updating first key without new default
+ final String testValue3 = Strings.repeat("A", 50);
+ settingsState.insertSettingLocked(testKey1, testValue3, null, false, testPackage);
+ expectedMemUsage -= testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test updating second key
+ settingsState.insertSettingLocked(testKey2, testValue2, null, false, testPackage);
+ expectedMemUsage -= testValue1.length() - testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test resetting key
+ settingsState.resetSettingLocked(testKey1);
+ expectedMemUsage += testValue2.length() - testValue3.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test resetting default value
+ settingsState.resetSettingDefaultValueLocked(testKey1);
+ expectedMemUsage -= testValue2.length();
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test deletion
+ settingsState.deleteSettingLocked(testKey2);
+ expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */;
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test another package with a different key
+ final String testPackage2 = testPackage + "2";
+ final String testKey3 = settingName + "3";
+ settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+ final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2;
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+
+ // Test system package
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, "android");
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+ assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+ assertEquals(0, settingsState.getMemoryUsage("android"));
+
+ // Test invalid value
+ try {
+ settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
+ testPackage);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+
+ // Test invalid key
+ try {
+ settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
+ testPackage);
+ fail("Should throw because it exceeded per package memory usage");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+ }
+ assertEquals(expectedMemUsage, settingsState.getMemoryUsage(testPackage));
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3ca6bf421fae..92c06d4b2d39 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -427,26 +427,28 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
UserState userState = getUserStateLocked(userId);
Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+ boolean anyServiceRemoved = false;
while (it.hasNext()) {
ComponentName comp = it.next();
String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
userState.mBindingServices.remove(comp);
- // Update the enabled services setting.
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- // Update the touch exploration granted services setting.
userState.mTouchExplorationGrantedServices.remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.
- TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- userState.mTouchExplorationGrantedServices, userId);
- onUserStateChangedLocked(userState);
- return;
+ anyServiceRemoved = true;
}
}
+ if (anyServiceRemoved) {
+ // Update the enabled services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ userState.mEnabledServices, userId);
+ // Update the touch exploration granted services setting.
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ userState.mTouchExplorationGrantedServices, userId);
+ onUserStateChangedLocked(userState);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 54504c3c1e24..44d67ed0d642 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -317,11 +317,16 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
resolvedType = key.requestResolvedType;
}
- // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
- // can specify a consistent launch mode even if the PendingIntent is immutable
+ // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
+ // to ensure that we can launch the pending intent with a consistent launch mode even
+ // if the provided PendingIntent is immutable (ie. to force an activity to launch into
+ // a new task, or to launch multiple instances if supported by the app)
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
- finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ // TODO(b/254490217): Move this check into SafeActivityOptions
+ if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
+ finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+ }
}
// Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20021db28aa6..3fb750023348 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12320,9 +12320,7 @@ public class PackageManagerService extends IPackageManager.Stub
AsyncTask.execute(() -> {
if (hasOldPkg) {
- mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
- allPackageNames, mPermissionCallback);
- mPermissionManager.revokeStoragePermissionsIfScopeExpanded(pkg, oldPkg,
+ mPermissionManager.onPackageUpdated(pkg, oldPkg, allPackageNames,
mPermissionCallback);
}
if (hasPermissionDefinitionChanges) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index c7fca39de562..3acf3594ca4c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -647,6 +647,45 @@ public class PermissionManagerService {
}
/**
+ * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and
+ * then updated past api 23, and the app does not satisfy any of the other SAW permission flags,
+ * the permission should be revoked.
+ *
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ private void revokeSystemAlertWindowIfUpgradedPast23(
+ @NonNull PackageParser.Package newPackage,
+ @NonNull PackageParser.Package oldPackage,
+ @NonNull PermissionCallback permissionCallback) {
+ if (oldPackage.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M
+ || newPackage.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
+ || !newPackage.requestedPermissions
+ .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
+ return;
+ }
+
+ BasePermission saw;
+ synchronized (mLock) {
+ saw = mSettings.getPermissionLocked(Manifest.permission.SYSTEM_ALERT_WINDOW);
+ }
+ final PackageSetting ps = (PackageSetting) newPackage.mExtras;
+ if (grantSignaturePermission(Manifest.permission.SYSTEM_ALERT_WINDOW, newPackage, saw,
+ ps.getPermissionsState())) {
+ return;
+ }
+ for (int userId: mUserManagerInt.getUserIds()) {
+ try {
+ revokeRuntimePermission(Manifest.permission.SYSTEM_ALERT_WINDOW,
+ newPackage.packageName, false, userId, permissionCallback);
+ } catch (IllegalStateException | SecurityException e) {
+ Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for "
+ + newPackage.packageName + " user " + userId, e);
+ }
+ }
+ }
+
+ /**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
* the new group auto-granted.
@@ -942,8 +981,8 @@ public class PermissionManagerService {
BasePermission bp = mSettings.getPermissionLocked(info.name);
added = bp == null;
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+ enforcePermissionCapLocked(info, tree);
if (added) {
- enforcePermissionCapLocked(info, tree);
bp = new BasePermission(info.name, tree.getSourcePackageName(),
BasePermission.TYPE_DYNAMIC);
} else if (!bp.isDynamic()) {
@@ -3170,25 +3209,22 @@ public class PermissionManagerService {
}
/**
- * If the app is updated, and has scoped storage permissions, then it is possible that the
- * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * If the app is updated, then some checks need to be performed to ensure the
+ * package is not attempting to expoit permission changes across API boundaries.
* @param newPackage The new package that was installed
* @param oldPackage The old package that was updated
+ * @param allPackageNames The current packages in the system
+ * @param permissionCallback Callback for permission changed
*/
- public void revokeStoragePermissionsIfScopeExpanded(
+ public void onPackageUpdated(
@NonNull PackageParser.Package newPackage,
@NonNull PackageParser.Package oldPackage,
+ @NonNull ArrayList<String> allPackageNames,
@NonNull PermissionCallback permissionCallback) {
PermissionManagerService.this.revokeStoragePermissionsIfScopeExpanded(newPackage,
oldPackage, permissionCallback);
- }
-
- @Override
- public void revokeRuntimePermissionsIfGroupChanged(
- @NonNull PackageParser.Package newPackage,
- @NonNull PackageParser.Package oldPackage,
- @NonNull ArrayList<String> allPackageNames,
- @NonNull PermissionCallback permissionCallback) {
+ PermissionManagerService.this.revokeSystemAlertWindowIfUpgradedPast23(newPackage,
+ oldPackage, permissionCallback);
PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
oldPackage, allPackageNames, permissionCallback);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 46fa4ffdbd45..a2f64eafe151 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -97,17 +97,15 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
public abstract void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate,
@NonNull Collection<PackageParser.Package> allPacakges, PermissionCallback callback);
- /**
- * We might auto-grant permissions if any permission of the group is already granted. Hence if
- * the group of a granted permission changes we need to revoke it to avoid having permissions of
- * the new group auto-granted.
- *
- * @param newPackage The new package that was installed
- * @param oldPackage The old package that was updated
- * @param allPackageNames All packages
- * @param permissionCallback Callback for permission changed
- */
- public abstract void revokeRuntimePermissionsIfGroupChanged(
+ /**
+ * If the app is updated, then some checks need to be performed to ensure the package is not
+ * attempting to expoit permission changes across API boundaries.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ * @param allPackageNames The current packages in the system
+ * @param permissionCallback Callback for permission changed
+ */
+ public abstract void onPackageUpdated(
@NonNull PackageParser.Package newPackage,
@NonNull PackageParser.Package oldPackage,
@NonNull ArrayList<String> allPackageNames,
@@ -127,17 +125,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager
@NonNull PermissionCallback permissionCallback);
/**
- * If the app is updated, and has scoped storage permissions, then it is possible that the
- * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
- * @param newPackage The new package that was installed
- * @param oldPackage The old package that was updated
- */
- public abstract void revokeStoragePermissionsIfScopeExpanded(
- @NonNull PackageParser.Package newPackage,
- @NonNull PackageParser.Package oldPackage,
- @NonNull PermissionCallback permissionCallback);
-
- /**
* Add all permissions in the given package.
* <p>
* NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 1eb7455135c7..900b59e0a8a2 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -84,7 +84,8 @@ class AppTaskImpl extends IAppTask.Stub {
if (tr == null) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
- return mService.getRecentTasks().createRecentTaskInfo(tr);
+ return mService.getRecentTasks().createRecentTaskInfo(tr,
+ true /* getTasksAllowed */);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 541a8bbc8865..56367f42886d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -944,7 +944,7 @@ class RecentTasks {
continue;
}
- final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr);
+ final ActivityManager.RecentTaskInfo rti = createRecentTaskInfo(tr, getTasksAllowed);
if (!getDetailedTasks) {
rti.baseIntent.replaceExtras((Bundle) null);
}
@@ -1715,12 +1715,15 @@ class RecentTasks {
/**
* Creates a new RecentTaskInfo from a TaskRecord.
*/
- ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) {
+ ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr, boolean getTasksAllowed) {
ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
tr.fillTaskInfo(rti);
// Fill in some deprecated values
rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
rti.persistentId = rti.taskId;
+ if (!getTasksAllowed) {
+ TaskRecord.trimIneffectiveInfo(tr, rti);
+ }
return rti;
}
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 3bf437d38bcc..20cb336a3030 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -68,7 +68,7 @@ class RunningTasks {
}
final TaskRecord task = iter.next();
- list.add(createRunningTaskInfo(task));
+ list.add(createRunningTaskInfo(task, allowed));
maxNum--;
}
}
@@ -76,11 +76,15 @@ class RunningTasks {
/**
* Constructs a {@link RunningTaskInfo} from a given {@param task}.
*/
- private RunningTaskInfo createRunningTaskInfo(TaskRecord task) {
+ private RunningTaskInfo createRunningTaskInfo(TaskRecord task, boolean allowed) {
final RunningTaskInfo rti = new RunningTaskInfo();
task.fillTaskInfo(rti);
// Fill in some deprecated values
rti.id = rti.taskId;
+
+ if (!allowed) {
+ TaskRecord.trimIneffectiveInfo(task, rti);
+ }
return rti;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 361f66e3106a..9de4c8121e4d 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -2437,6 +2437,40 @@ class TaskRecord extends ConfigurationContainer {
}
/**
+ * Removes the activity info if the activity belongs to a different uid, which is
+ * different from the app that hosts the task.
+ */
+ static void trimIneffectiveInfo(TaskRecord task, TaskInfo info) {
+ int topActivityUid = task.effectiveUid;
+ for (int i = task.mActivities.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.finishing || r.isState(ActivityState.INITIALIZING)) {
+ continue;
+ }
+ topActivityUid = r.info.applicationInfo.uid;
+ break;
+ }
+
+ if (task.effectiveUid != topActivityUid) {
+ info.topActivity = null;
+ }
+
+ int baseActivityUid = task.effectiveUid;
+ for (int i = 0; i < task.mActivities.size(); ++i) {
+ final ActivityRecord r = task.mActivities.get(i);
+ if (r.finishing) {
+ continue;
+ }
+ baseActivityUid = r.info.applicationInfo.uid;
+ break;
+ }
+
+ if (task.effectiveUid != baseActivityUid) {
+ info.baseActivity = null;
+ }
+ }
+
+ /**
* Returns a {@link TaskInfo} with information from this task.
*/
ActivityManager.RunningTaskInfo getTaskInfo() {