Merge changes from topic "AlertSlider-universe" into universe
* changes:
fixup! Add Alert Slider user interface [SQUASHED]
Add Alert Slider user interface [SQUASHED]
DisplayUtils: Introduce getScaleFactor
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f5c4720..adde547 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -127,6 +127,7 @@
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
+import android.os.IBinderCallback;
import android.os.ICancellationSignal;
import android.os.LocaleList;
import android.os.Looper;
@@ -359,6 +360,15 @@
/** Maps from activity token to the pending override configuration. */
@GuardedBy("mPendingOverrideConfigs")
private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
+
+ /**
+ * A queue of pending ApplicationInfo updates. In case when we get a concurrent update
+ * this queue allows us to only apply the latest object, and it can be applied on demand
+ * instead of waiting for the handler thread to reach the scheduled callback.
+ */
+ @GuardedBy("mResourcesManager")
+ private final ArrayMap<String, ApplicationInfo> mPendingAppInfoUpdates = new ArrayMap<>();
+
/** The activities to be truly destroyed (not include relaunch). */
final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
@@ -1260,9 +1270,19 @@
}
public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+ synchronized (mResourcesManager) {
+ var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai);
+ if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) {
+ Slog.w(TAG, "Skipping application info changed for obsolete AI with TS "
+ + ai.createTimestamp + " < already pending TS "
+ + oldAi.createTimestamp);
+ mPendingAppInfoUpdates.put(ai.packageName, oldAi);
+ return;
+ }
+ }
mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai);
- mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai);
- sendMessage(H.APPLICATION_INFO_CHANGED, ai);
+ mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName);
+ sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName);
}
public void updateTimeZone() {
@@ -2437,7 +2457,7 @@
break;
}
case APPLICATION_INFO_CHANGED:
- handleApplicationInfoChanged((ApplicationInfo) msg.obj);
+ applyPendingApplicationInfoChanges((String) msg.obj);
break;
case RUN_ISOLATED_ENTRY_POINT:
handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
@@ -3922,7 +3942,8 @@
mProfiler.startProfiling();
}
- // Make sure we are running with the most recent config.
+ // Make sure we are running with the most recent config and resource paths.
+ applyPendingApplicationInfoChanges(r.activityInfo.packageName);
mConfigurationController.handleConfigurationChanged(null, null);
updateDeviceIdForNonUIContexts(deviceId);
@@ -6249,6 +6270,17 @@
r.mLastReportedWindowingMode = newWindowingMode;
}
+ private void applyPendingApplicationInfoChanges(String packageName) {
+ final ApplicationInfo ai;
+ synchronized (mResourcesManager) {
+ ai = mPendingAppInfoUpdates.remove(packageName);
+ }
+ if (ai == null) {
+ return;
+ }
+ handleApplicationInfoChanged(ai);
+ }
+
/**
* Updates the application info.
*
@@ -6274,6 +6306,16 @@
apk = ref != null ? ref.get() : null;
ref = mResourcePackages.get(ai.packageName);
resApk = ref != null ? ref.get() : null;
+ for (ActivityClientRecord ar : mActivities.values()) {
+ if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) {
+ ar.activityInfo.applicationInfo = ai;
+ if (apk != null || resApk != null) {
+ ar.packageInfo = apk != null ? apk : resApk;
+ } else {
+ apk = ar.packageInfo;
+ }
+ }
+ }
}
if (apk != null) {
@@ -7056,6 +7098,18 @@
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
+
+ // Set binder transaction callback after finishing bindApplication
+ Binder.setTransactionCallback(new IBinderCallback() {
+ @Override
+ public void onTransactionError(int pid, int code, int flags, int err) {
+ try {
+ mgr.frozenBinderTransactionDetected(pid, code, flags, err);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ });
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index d859f3f..24cb9ea 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -460,6 +460,33 @@
*/
public static final int SUBREASON_SDK_SANDBOX_NOT_NEEDED = 28;
+ /**
+ * The process was killed because the binder proxy limit for system server was exceeded.
+ *
+ * For internal use only.
+ * @hide
+ */
+ public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29;
+
+ /**
+ * The process was killed by the [kernel] Out-of-memory (OOM) killer; this
+ * would be set only when the reason is {@link #REASON_LOW_MEMORY}.
+ *
+ * For internal use only.
+ * @hide
+ */
+ public static final int SUBREASON_OOM_KILL = 30;
+
+ /**
+ * The process was killed because its async kernel binder buffer is running out
+ * while being frozen.
+ * this would be set only when the reason is {@link #REASON_FREEZER}.
+ *
+ * For internal use only.
+ * @hide
+ */
+ public static final int SUBREASON_FREEZER_BINDER_ASYNC_FULL = 31;
+
// If there is any OEM code which involves additional app kill reasons, it should
// be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
@@ -635,6 +662,9 @@
SUBREASON_KILL_BACKGROUND,
SUBREASON_PACKAGE_UPDATE,
SUBREASON_UNDELIVERED_BROADCAST,
+ SUBREASON_EXCESSIVE_BINDER_OBJECTS,
+ SUBREASON_OOM_KILL,
+ SUBREASON_FREEZER_BINDER_ASYNC_FULL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SubReason {}
@@ -1360,6 +1390,12 @@
return "PACKAGE UPDATE";
case SUBREASON_UNDELIVERED_BROADCAST:
return "UNDELIVERED BROADCAST";
+ case SUBREASON_EXCESSIVE_BINDER_OBJECTS:
+ return "EXCESSIVE BINDER OBJECTS";
+ case SUBREASON_OOM_KILL:
+ return "OOM KILL";
+ case SUBREASON_FREEZER_BINDER_ASYNC_FULL:
+ return "FREEZER BINDER ASYNC FULL";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7cc0b17..af824f8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -811,6 +811,8 @@
};
private static final String[] pTensorCodenames = {
+ "husky",
+ "shiba",
"felix",
"tangorpro",
"lynx",
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f60213e..4d43a24 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -926,6 +926,16 @@
int[] getUidFrozenState(in int[] uids);
/**
+ * Notify AMS about binder transactions to frozen apps.
+ *
+ * @param debugPid The binder transaction sender
+ * @param code The binder transaction code
+ * @param flags The binder transaction flags
+ * @param err The binder transaction error
+ */
+ oneway void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err);
+
+ /**
* Should disable touch if three fingers to screen shot is active?
*/
boolean isSwipeToScreenshotGestureActive();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 7ef8972..9d70161 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -343,7 +343,9 @@
*/
public void updateApplicationInfo(@NonNull ApplicationInfo aInfo,
@Nullable List<String> oldPaths) {
- setApplicationInfo(aInfo);
+ if (!setApplicationInfo(aInfo)) {
+ return;
+ }
final List<String> newPaths = new ArrayList<>();
makePaths(mActivityThread, aInfo, newPaths);
@@ -388,7 +390,13 @@
mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader);
}
- private void setApplicationInfo(ApplicationInfo aInfo) {
+ private boolean setApplicationInfo(ApplicationInfo aInfo) {
+ if (mApplicationInfo != null && mApplicationInfo.createTimestamp > aInfo.createTimestamp) {
+ Slog.w(TAG, "New application info for package " + aInfo.packageName
+ + " is out of date with TS " + aInfo.createTimestamp + " < the current TS "
+ + mApplicationInfo.createTimestamp);
+ return false;
+ }
final int myUid = Process.myUid();
aInfo = adjustNativeLibraryPaths(aInfo);
mApplicationInfo = aInfo;
@@ -411,6 +419,7 @@
if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
}
+ return true;
}
void setSdkSandboxStorage(@Nullable String sdkSandboxClientAppVolumeUuid,
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 01e8fea..b5a7c9b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -642,6 +642,32 @@
*/
public static final native void blockUntilThreadAvailable();
+
+ /**
+ * TODO (b/308179628): Move this to libbinder for non-Java usages.
+ */
+ private static IBinderCallback sBinderCallback = null;
+
+ /**
+ * Set callback function for unexpected binder transaction errors.
+ *
+ * @hide
+ */
+ public static final void setTransactionCallback(IBinderCallback callback) {
+ sBinderCallback = callback;
+ }
+
+ /**
+ * Execute the callback function if it's already set.
+ *
+ * @hide
+ */
+ public static final void transactionCallback(int pid, int code, int flags, int err) {
+ if (sBinderCallback != null) {
+ sBinderCallback.onTransactionError(pid, code, flags, err);
+ }
+ }
+
/**
* Default constructor just initializes the object.
*
diff --git a/core/java/android/os/IBinderCallback.java b/core/java/android/os/IBinderCallback.java
new file mode 100644
index 0000000..e4be5b0
--- /dev/null
+++ b/core/java/android/os/IBinderCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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.os;
+
+/**
+ * Callback interface for binder transaction errors
+ *
+ * @hide
+ */
+public interface IBinderCallback {
+ /**
+ * Callback function for unexpected binder transaction errors.
+ *
+ * @param debugPid The binder transaction sender
+ * @param code The binder transaction code
+ * @param flags The binder transaction flags
+ * @param err The binder transaction error
+ */
+ void onTransactionError(int debugPid, int code, int flags, int err);
+}
diff --git a/core/java/com/android/internal/os/BinderfsStatsReader.java b/core/java/com/android/internal/os/BinderfsStatsReader.java
new file mode 100644
index 0000000..9cc4a35
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderfsStatsReader.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.internal.os;
+
+import com.android.internal.util.ProcFileReader;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Reads and parses {@code binder_logs/stats} file in the {@code binderfs} filesystem.
+ * Reuse procFileReader as the contents are generated by Linux kernel in the same way.
+ *
+ * A typical example of binderfs stats log
+ *
+ * binder stats:
+ * BC_TRANSACTION: 378004
+ * BC_REPLY: 268352
+ * BC_FREE_BUFFER: 665854
+ * ...
+ * proc 12645
+ * context binder
+ * threads: 12
+ * requested threads: 0+5/15
+ * ready threads 0
+ * free async space 520192
+ * ...
+ */
+public class BinderfsStatsReader {
+ private final String mPath;
+
+ public BinderfsStatsReader() {
+ mPath = "/dev/binderfs/binder_logs/stats";
+ }
+
+ public BinderfsStatsReader(String path) {
+ mPath = path;
+ }
+
+ /**
+ * Read binderfs stats and call the consumer(pid, free) function for each valid process
+ *
+ * @param predicate Test if the pid is valid.
+ * @param biConsumer Callback function for each valid pid and its free async space
+ * @param consumer The error function to deal with exceptions
+ */
+ public void handleFreeAsyncSpace(Predicate<Integer> predicate,
+ BiConsumer<Integer, Integer> biConsumer, Consumer<Exception> consumer) {
+ try (ProcFileReader mReader = new ProcFileReader(new FileInputStream(mPath))) {
+ while (mReader.hasMoreData()) {
+ // find the next process
+ if (!mReader.nextString().equals("proc")) {
+ mReader.finishLine();
+ continue;
+ }
+
+ // read pid
+ int pid = mReader.nextInt();
+ mReader.finishLine();
+
+ // check if we have interest in this process
+ if (!predicate.test(pid)) {
+ continue;
+ }
+
+ // read free async space
+ mReader.finishLine(); // context binder
+ mReader.finishLine(); // threads:
+ mReader.finishLine(); // requested threads:
+ mReader.finishLine(); // ready threads
+ if (!mReader.nextString().equals("free")) {
+ mReader.finishLine();
+ continue;
+ }
+ if (!mReader.nextString().equals("async")) {
+ mReader.finishLine();
+ continue;
+ }
+ if (!mReader.nextString().equals("space")) {
+ mReader.finishLine();
+ continue;
+ }
+ int free = mReader.nextInt();
+ mReader.finishLine();
+ biConsumer.accept(pid, free);
+ }
+ } catch (IOException | NumberFormatException e) {
+ consumer.accept(e);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/bliss/AttestationHooks.java b/core/java/com/android/internal/util/bliss/AttestationHooks.java
index 6286b60..7b31fc8 100644
--- a/core/java/com/android/internal/util/bliss/AttestationHooks.java
+++ b/core/java/com/android/internal/util/bliss/AttestationHooks.java
@@ -64,7 +64,7 @@
"DEVICE", "husky",
"PRODUCT", "husky",
"MODEL", "Pixel 8 Pro",
- "FINGERPRINT", "google/husky/husky:14/UD1A.230803.041/10808477:user/release-keys"
+ "FINGERPRINT", "google/husky/husky:14/UQ1A.240105.004/11206848:user/release-keys"
);
private static volatile String sProcessName;
diff --git a/core/java/com/android/internal/util/bliss/PixelPropsUtils.java b/core/java/com/android/internal/util/bliss/PixelPropsUtils.java
index e35aa32..761584b 100644
--- a/core/java/com/android/internal/util/bliss/PixelPropsUtils.java
+++ b/core/java/com/android/internal/util/bliss/PixelPropsUtils.java
@@ -1,8 +1,6 @@
/*
* Copyright (C) 2020 The Pixel Experience Project
- * 2022 StatiXOS
- * 2021-2022 crDroid Android Project
- * 2019-2023 Evolution X
+ * 2021-2023 crDroid Android Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +45,7 @@
private static final Map<String, Object> propsToChangeGeneric;
private static final Map<String, Object> propsToChangePixel5;
- private static final Map<String, Object> propsToChangePixel7Pro;
+ private static final Map<String, Object> propsToChangePixel8Pro;
private static final Map<String, Object> propsToChangePixelXL;
private static final Map<String, Object> propsToChangeROG6;
private static final Map<String, Object> propsToChangeXP5;
@@ -59,7 +57,7 @@
private static final Map<String, ArrayList<String>> propsToKeep;
// Packages to Spoof as Pixel 7 Pro
- private static final String[] packagesToChangePixel7Pro = {
+ private static final String[] packagesToChangePixel8Pro = {
"com.google.android.apps.privacy.wildlife",
"com.google.android.apps.wallpaper",
"com.google.android.apps.wallpaper.pixel",
@@ -178,15 +176,15 @@
propsToChangeGeneric = new HashMap<>();
propsToChangeGeneric.put("TYPE", "user");
propsToChangeGeneric.put("TAGS", "release-keys");
- propsToChangePixel7Pro = new HashMap<>();
- propsToChangePixel7Pro.put("BRAND", "google");
- propsToChangePixel7Pro.put("MANUFACTURER", "Google");
- propsToChangePixel7Pro.put("DEVICE", "cheetah");
- propsToChangePixel7Pro.put("PRODUCT", "cheetah");
- propsToChangePixel7Pro.put("HARDWARE", "cheetah");
- propsToChangePixel7Pro.put("MODEL", "Pixel 7 Pro");
- propsToChangePixel7Pro.put("ID", "UP1A.231105.003");
- propsToChangePixel7Pro.put("FINGERPRINT", "google/cheetah/cheetah:14/UP1A.231105.003/11010452:user/release-keys");
+ propsToChangePixel8Pro = new HashMap<>();
+ propsToChangePixel8Pro.put("BRAND", "google");
+ propsToChangePixel8Pro.put("MANUFACTURER", "Google");
+ propsToChangePixel8Pro.put("DEVICE", "husky");
+ propsToChangePixel8Pro.put("PRODUCT", "husky");
+ propsToChangePixel8Pro.put("HARDWARE", "husky");
+ propsToChangePixel8Pro.put("MODEL", "Pixel 8 Pro");
+ propsToChangePixel8Pro.put("ID", "UQ1A.240105.004");
+ propsToChangePixel8Pro.put("FINGERPRINT", "google/husky/husky:14/UQ1A.240105.004/11206848:user/release-keys");
propsToChangePixel5 = new HashMap<>();
propsToChangePixel5.put("BRAND", "google");
propsToChangePixel5.put("MANUFACTURER", "Google");
@@ -279,8 +277,8 @@
sIsFinsky = true;
return;
} else {
- if (Arrays.asList(packagesToChangePixel7Pro).contains(packageName)) {
- propsToChange.putAll(propsToChangePixel7Pro);
+ if (Arrays.asList(packagesToChangePixel8Pro).contains(packageName)) {
+ propsToChange.putAll(propsToChangePixel8Pro);
} else if (Arrays.asList(packagesToChangePixelXL).contains(packageName)) {
propsToChange.putAll(propsToChangePixelXL);
} else {
@@ -434,3 +432,4 @@
}
}
}
+
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index b24dc8a..3b7ba68 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -75,6 +75,7 @@
jclass mClass;
jmethodID mExecTransact;
jmethodID mGetInterfaceDescriptor;
+ jmethodID mTransactionCallback;
// Object state.
jfieldID mObject;
@@ -1121,6 +1122,8 @@
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
gBinderOffsets.mGetInterfaceDescriptor = GetMethodIDOrDie(env, clazz, "getInterfaceDescriptor",
"()Ljava/lang/String;");
+ gBinderOffsets.mTransactionCallback =
+ GetStaticMethodIDOrDie(env, clazz, "transactionCallback", "(IIII)V");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
return RegisterMethodsOrDie(
@@ -1452,7 +1455,12 @@
if (err == NO_ERROR) {
return JNI_TRUE;
- } else if (err == UNKNOWN_TRANSACTION) {
+ }
+
+ env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback, getpid(),
+ code, flags, err);
+
+ if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a5d27df..f53ebdf 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8273,6 +8273,10 @@
android:exported="true">
</provider>
+ <meta-data
+ android:name="com.android.server.patch.25239169"
+ android:value="true" />
+
</application>
</manifest>
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
new file mode 100644
index 0000000..5498083
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BinderfsStatsReaderTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2023 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.util.IntArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BinderfsStatsReaderTest {
+ private static final String BINDER_LOGS_STATS_HEADER = """
+ binder stats:
+ BC_TRANSACTION: 695756
+ BC_REPLY: 547779
+ BC_FREE_BUFFER: 1283223
+ BR_FAILED_REPLY: 4
+ BR_FROZEN_REPLY: 3
+ BR_ONEWAY_SPAM_SUSPECT: 1
+ proc: active 313 total 377
+ thread: active 3077 total 5227
+ """;
+ private static final String BINDER_LOGS_STATS_PROC1 = """
+ proc 14505
+ context binder
+ threads: 4
+ requested threads: 0+2/15
+ ready threads 0
+ free async space 520192
+ nodes: 9
+ refs: 29 s 29 w 29
+ buffers: 0
+ """;
+ private static final String BINDER_LOGS_STATS_PROC2 = """
+ proc 14461
+ context binder
+ threads: 8
+ requested threads: 0+2/15
+ ready threads 0
+ free async space 62
+ nodes: 30
+ refs: 51 s 51 w 51
+ buffers: 0
+ """;
+ private static final String BINDER_LOGS_STATS_PROC3 = """
+ proc 542
+ context binder
+ threads: 2
+ requested threads: 0+0/15
+ ready threads 0
+ free async space 519896
+ nodes: 1
+ refs: 2 s 3 w 2
+ buffers: 1
+ """;
+ private static final String BINDER_LOGS_STATS_PROC4 = """
+ proc 540
+ context binder
+ threads: 1
+ requested threads: 0+0/0
+ ready threads 1
+ free async space 44
+ nodes: 4
+ refs: 1 s 1 w 1
+ buffers: 0
+ """;
+ private File mStatsDirectory;
+ private int mFreezerBinderAsyncThreshold;
+ private IntArray mValidPids; // The pool of valid pids
+ private IntArray mStatsPids; // The pids read from binderfs stats that are also valid
+ private IntArray mStatsFree; // The free async space of the above pids
+ private boolean mHasError;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ mStatsDirectory = context.getDir("binder_logs", Context.MODE_PRIVATE);
+ mFreezerBinderAsyncThreshold = 1024;
+ mValidPids = IntArray.fromArray(new int[]{14505, 14461, 542, 540}, 4);
+ mStatsPids = new IntArray();
+ mStatsFree = new IntArray();
+ mHasError = false;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mStatsDirectory);
+ }
+
+ @Test
+ public void testNoneProc() throws Exception {
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER);
+ assertFalse(mHasError);
+ assertEquals(0, mStatsPids.size());
+ assertEquals(0, mStatsFree.size());
+ }
+
+ @Test
+ public void testOneProc() throws Exception {
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1);
+ assertFalse(mHasError);
+ assertEquals(0, mStatsPids.size());
+ assertEquals(0, mStatsFree.size());
+ }
+
+ @Test
+ public void testTwoProc() throws Exception {
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ + BINDER_LOGS_STATS_PROC2);
+ assertFalse(mHasError);
+ assertArrayEquals(mStatsPids.toArray(), new int[]{14461});
+ assertArrayEquals(mStatsFree.toArray(), new int[]{62});
+ }
+
+ @Test
+ public void testThreeProc() throws Exception {
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ + BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3);
+ assertFalse(mHasError);
+ assertArrayEquals(mStatsPids.toArray(), new int[]{14461});
+ assertArrayEquals(mStatsFree.toArray(), new int[]{62});
+ }
+
+ @Test
+ public void testFourProc() throws Exception {
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ + BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3 + BINDER_LOGS_STATS_PROC4);
+ assertFalse(mHasError);
+ assertArrayEquals(mStatsPids.toArray(), new int[]{14461, 540});
+ assertArrayEquals(mStatsFree.toArray(), new int[]{62, 44});
+ }
+
+ @Test
+ public void testInvalidProc() throws Exception {
+ mValidPids = new IntArray();
+ runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ + BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3 + BINDER_LOGS_STATS_PROC4);
+ assertFalse(mHasError);
+ assertEquals(0, mStatsPids.size());
+ assertEquals(0, mStatsFree.size());
+ }
+
+ private void runHandleBlockingFileLocks(String fileContents) throws Exception {
+ File tempFile = File.createTempFile("stats", null, mStatsDirectory);
+ Files.write(tempFile.toPath(), fileContents.getBytes());
+ new BinderfsStatsReader(tempFile.toString()).handleFreeAsyncSpace(
+ // Check if the current process is a valid one
+ pid -> mValidPids.indexOf(pid) != -1,
+
+ // Check if the current process is running out of async binder space
+ (pid, free) -> {
+ if (free < mFreezerBinderAsyncThreshold) {
+ mStatsPids.add(pid);
+ mStatsFree.add(free);
+ }
+ },
+
+ // Log the error if binderfs stats can't be accesses or correctly parsed
+ exception -> mHasError = true);
+ Files.delete(tempFile.toPath());
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index c95c9f0..1c79239 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2244,6 +2244,25 @@
return SPLIT_POSITION_UNDEFINED;
}
+ /**
+ * Returns the {@link StageType} where {@param token} is being used
+ * {@link SplitScreen#STAGE_TYPE_UNDEFINED} otherwise
+ */
+ @StageType
+ public int getSplitItemStage(@Nullable WindowContainerToken token) {
+ if (token == null) {
+ return STAGE_TYPE_UNDEFINED;
+ }
+
+ if (mMainStage.containsToken(token)) {
+ return STAGE_TYPE_MAIN;
+ } else if (mSideStage.containsToken(token)) {
+ return STAGE_TYPE_SIDE;
+ }
+
+ return STAGE_TYPE_UNDEFINED;
+ }
+
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
final StageTaskListener topLeftStage =
@@ -2491,7 +2510,16 @@
mRecentTasks.ifPresent(
recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
}
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT);
+ @StageType int topStage = STAGE_TYPE_UNDEFINED;
+ if (isSplitScreenVisible()) {
+ // Get the stage where a child exists to keep that stage onTop
+ if (mMainStage.getChildCount() != 0 && mSideStage.getChildCount() == 0) {
+ topStage = STAGE_TYPE_MAIN;
+ } else if (mSideStage.getChildCount() != 0 && mMainStage.getChildCount() == 0) {
+ topStage = STAGE_TYPE_SIDE;
+ }
+ }
+ prepareExitSplitScreen(topStage, outWCT);
}
}
@@ -2908,7 +2936,11 @@
return SPLIT_POSITION_UNDEFINED;
}
- /** Synchronize split-screen state with transition and make appropriate preparations. */
+ /**
+ * Synchronize split-screen state with transition and make appropriate preparations.
+ * @param toStage The stage that will not be dismissed. If set to
+ * {@link SplitScreen#STAGE_TYPE_UNDEFINED} then both stages will be dismissed
+ */
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 4897c4e..f0bb665 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.sysui.ShellInit;
@@ -511,8 +512,26 @@
// make a new startTransaction because pip's startEnterAnimation "consumes" it so
// we need a separate one to send over to launcher.
SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
+ @SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
+ if (mSplitHandler.isSplitScreenVisible()) {
+ // The non-going home case, we could be pip-ing one of the split stages and keep
+ // showing the other
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == pipChange) {
+ // Ignore the change/task that's going into Pip
+ continue;
+ }
+ @SplitScreen.StageType int splitItemStage =
+ mSplitHandler.getSplitItemStage(change.getLastParent());
+ if (splitItemStage != STAGE_TYPE_UNDEFINED) {
+ topStageToKeep = splitItemStage;
+ break;
+ }
+ }
+ }
// Let split update internal state for dismiss.
- mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED,
+ mSplitHandler.prepareDismissAnimation(topStageToKeep,
EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
finishTransaction);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 84ebb84..cd58ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -231,7 +231,6 @@
animation.removeEndListener(this)
if (!canceled) {
-
// The delay between finishing this animation and starting the runnable
val delay = max(0, runnableDelay - elapsedTimeSinceEntry)
@@ -461,7 +460,6 @@
}
private fun handleMoveEvent(event: MotionEvent) {
-
val x = event.x
val y = event.y
@@ -927,17 +925,7 @@
GestureState.ACTIVE -> {
previousXTranslationOnActiveOffset = previousXTranslation
updateRestingArrowDimens()
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
- )
- } else {
- vibratorHelper.cancel()
- mainHandler.postDelayed(10L) {
- vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
- }
- }
+ performActivatedHapticFeedback()
val popVelocity =
if (previousState == GestureState.INACTIVE) {
POP_ON_INACTIVE_TO_ACTIVE_VELOCITY
@@ -958,25 +946,24 @@
mView.popOffEdge(POP_ON_INACTIVE_VELOCITY)
- if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
- vibratorHelper.performHapticFeedback(
- mView,
- HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
- )
- } else {
- vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
- }
+ performDeactivatedHapticFeedback()
updateRestingArrowDimens()
}
GestureState.FLUNG -> {
+ // Typically a vibration is only played while transitioning to ACTIVE. However there
+ // are instances where a fling to trigger back occurs while not in that state.
+ // (e.g. A fling is detected before crossing the trigger threshold.)
+ if (previousState != GestureState.ACTIVE) {
+ performActivatedHapticFeedback()
+ }
mainHandler.postDelayed(POP_ON_FLING_DELAY) {
mView.popScale(POP_ON_FLING_VELOCITY)
}
- updateRestingArrowDimens()
mainHandler.postDelayed(
onEndSetCommittedStateListener.runnable,
MIN_DURATION_FLING_ANIMATION
)
+ updateRestingArrowDimens()
}
GestureState.COMMITTED -> {
// In most cases, animating between states is handled via `updateRestingArrowDimens`
@@ -1011,6 +998,31 @@
}
}
+ private fun performDeactivatedHapticFeedback() {
+ if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+ )
+ } else {
+ vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
+ }
+ }
+
+ private fun performActivatedHapticFeedback() {
+ if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
+ vibratorHelper.performHapticFeedback(
+ mView,
+ HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+ )
+ } else {
+ vibratorHelper.cancel()
+ mainHandler.postDelayed(10L) {
+ vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
+ }
+ }
+ }
+
private fun convertVelocityToAnimationFactor(
valueOnFastVelocity: Float,
valueOnSlowVelocity: Float,
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 98614b6..0c6d053 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -687,8 +687,7 @@
public PendingIntent requestNotificationAccess(ComponentName component, int userId)
throws RemoteException {
String callingPackage = component.getPackageName();
- checkCanCallNotificationApi(callingPackage);
- // TODO: check userId.
+ checkCanCallNotificationApi(callingPackage, userId);
if (component.flattenToString().length() > MAX_CN_LENGTH) {
throw new IllegalArgumentException("Component name is too long.");
}
@@ -714,7 +713,7 @@
@Deprecated
@Override
public boolean hasNotificationAccess(ComponentName component) throws RemoteException {
- checkCanCallNotificationApi(component.getPackageName());
+ checkCanCallNotificationApi(component.getPackageName(), getCallingUserId());
NotificationManager nm = getContext().getSystemService(NotificationManager.class);
return nm.isNotificationListenerAccessGranted(component);
}
@@ -928,8 +927,7 @@
createNewAssociation(userId, packageName, macAddressObj, null, null, false);
}
- private void checkCanCallNotificationApi(String callingPackage) {
- final int userId = getCallingUserId();
+ private void checkCanCallNotificationApi(String callingPackage, int userId) {
enforceCallerIsSystemOr(userId, callingPackage);
if (getCallingUid() == SYSTEM_UID) return;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8e1e3d8..fb7a81b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -71,6 +71,7 @@
import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
@@ -1415,7 +1416,13 @@
private void purgeOldGrants(UserAccounts accounts) {
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
- List<Integer> uids = accounts.accountsDb.findAllUidGrants();
+ List<Integer> uids;
+ try {
+ uids = accounts.accountsDb.findAllUidGrants();
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.w(TAG, "Could not delete grants for user = " + accounts.userId);
+ return;
+ }
for (int uid : uids) {
final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
if (packageExists) {
@@ -1441,7 +1448,13 @@
mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
} catch (NameNotFoundException e) {
// package does not exist - remove visibility values
- accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
+ try {
+ accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
+ } catch (SQLiteCantOpenDatabaseException sqlException) {
+ Log.w(TAG, "Could not delete account visibility for user = "
+ + accounts.userId, sqlException);
+ continue;
+ }
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
for (Account account : accounts.visibilityCache.keySet()) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c77248..f158e335 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5436,7 +5436,20 @@
intent = new Intent(Intent.ACTION_MAIN);
}
try {
- target.send(code, intent, resolvedType, allowlistToken, null,
+ if (allowlistToken != null) {
+ final int callingUid = Binder.getCallingUid();
+ final String packageName;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target."
+ + " Calling package: " + packageName + "; intent: " + intent
+ + "; options: " + options);
+ }
+ target.send(code, intent, resolvedType, null, null,
requiredPermission, options);
} catch (RemoteException e) {
}
@@ -20116,7 +20129,7 @@
final long token = Binder.clearCallingIdentity();
try {
- return mOomAdjuster.mCachedAppOptimizer.isFreezerSupported();
+ return CachedAppOptimizer.isFreezerSupported();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -20277,4 +20290,21 @@
return mIsSwipeToScreenshotEnabled && SystemProperties.getBoolean("sys.android.screenshot", false);
}
}
+
+ /**
+ * Deal with binder transactions to frozen apps.
+ *
+ * @param debugPid The binder transaction sender
+ * @param code The binder transaction code
+ * @param flags The binder transaction flags
+ * @param err The binder transaction error
+ */
+ @Override
+ public void frozenBinderTransactionDetected(int debugPid, int code, int flags, int err) {
+ final ProcessRecord app;
+ synchronized (mPidsSelfLocked) {
+ app = mPidsSelfLocked.get(debugPid);
+ }
+ mOomAdjuster.mCachedAppOptimizer.binderError(debugPid, app, code, flags, err);
+ }
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index db93323..70ed916 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -52,6 +52,8 @@
import android.app.ActivityManagerInternal.OomAdjReason;
import android.app.ActivityThread;
import android.app.ApplicationExitInfo;
+import android.app.ApplicationExitInfo.Reason;
+import android.app.ApplicationExitInfo.SubReason;
import android.app.IApplicationThread;
import android.database.ContentObserver;
import android.net.Uri;
@@ -67,6 +69,7 @@
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Pair;
@@ -75,6 +78,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BinderfsStatsReader;
import com.android.internal.os.ProcLocksReader;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ServiceThread;
@@ -131,6 +135,12 @@
"freeze_binder_offset";
@VisibleForTesting static final String KEY_FREEZER_BINDER_THRESHOLD =
"freeze_binder_threshold";
+ @VisibleForTesting static final String KEY_FREEZER_BINDER_CALLBACK_ENABLED =
+ "freeze_binder_callback_enabled";
+ @VisibleForTesting static final String KEY_FREEZER_BINDER_CALLBACK_THROTTLE =
+ "freeze_binder_callback_throttle";
+ @VisibleForTesting static final String KEY_FREEZER_BINDER_ASYNC_THRESHOLD =
+ "freeze_binder_async_threshold";
static final int UNFREEZE_REASON_NONE =
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_NONE;
@@ -269,6 +279,9 @@
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4;
@VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500;
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_THRESHOLD = 1_000;
+ @VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_CALLBACK_ENABLED = true;
+ @VisibleForTesting static final long DEFAULT_FREEZER_BINDER_CALLBACK_THROTTLE = 10_000L;
+ @VisibleForTesting static final int DEFAULT_FREEZER_BINDER_ASYNC_THRESHOLD = 1_024;
@VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.CACHED_APPS_FREEZER_ENABLED);
@@ -311,6 +324,7 @@
static final int COMPACT_NATIVE_MSG = 5;
static final int UID_FROZEN_STATE_CHANGED_MSG = 6;
static final int DEADLOCK_WATCHDOG_MSG = 7;
+ static final int BINDER_ERROR_MSG = 8;
// When free swap falls below this percentage threshold any full (file + anon)
// compactions will be downgraded to file only compactions to reduce pressure
@@ -407,7 +421,10 @@
} else if (KEY_FREEZER_BINDER_ENABLED.equals(name)
|| KEY_FREEZER_BINDER_DIVISOR.equals(name)
|| KEY_FREEZER_BINDER_THRESHOLD.equals(name)
- || KEY_FREEZER_BINDER_OFFSET.equals(name)) {
+ || KEY_FREEZER_BINDER_OFFSET.equals(name)
+ || KEY_FREEZER_BINDER_CALLBACK_ENABLED.equals(name)
+ || KEY_FREEZER_BINDER_CALLBACK_THROTTLE.equals(name)
+ || KEY_FREEZER_BINDER_ASYNC_THRESHOLD.equals(name)) {
updateFreezerBinderState();
}
}
@@ -479,7 +496,15 @@
@VisibleForTesting volatile int mFreezerBinderOffset = DEFAULT_FREEZER_BINDER_OFFSET;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mFreezerBinderThreshold = DEFAULT_FREEZER_BINDER_THRESHOLD;
-
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting volatile boolean mFreezerBinderCallbackEnabled =
+ DEFAULT_FREEZER_BINDER_CALLBACK_ENABLED;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting volatile long mFreezerBinderCallbackThrottle =
+ DEFAULT_FREEZER_BINDER_CALLBACK_THROTTLE;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting volatile int mFreezerBinderAsyncThreshold =
+ DEFAULT_FREEZER_BINDER_ASYNC_THRESHOLD;
// Handler on which compaction runs.
@VisibleForTesting
@@ -487,6 +512,7 @@
private Handler mFreezeHandler;
@GuardedBy("mProcLock")
private boolean mFreezerOverride = false;
+ private long mFreezerBinderCallbackLast = -1;
@VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
@VisibleForTesting volatile boolean mFreezerExemptInstPkg = DEFAULT_FREEZER_EXEMPT_INST_PKG;
@@ -789,6 +815,12 @@
pw.println(" " + KEY_FREEZER_BINDER_THRESHOLD + "=" + mFreezerBinderThreshold);
pw.println(" " + KEY_FREEZER_BINDER_DIVISOR + "=" + mFreezerBinderDivisor);
pw.println(" " + KEY_FREEZER_BINDER_OFFSET + "=" + mFreezerBinderOffset);
+ pw.println(" " + KEY_FREEZER_BINDER_CALLBACK_ENABLED + "="
+ + mFreezerBinderCallbackEnabled);
+ pw.println(" " + KEY_FREEZER_BINDER_CALLBACK_THROTTLE + "="
+ + mFreezerBinderCallbackThrottle);
+ pw.println(" " + KEY_FREEZER_BINDER_ASYNC_THRESHOLD + "="
+ + mFreezerBinderAsyncThreshold);
synchronized (mProcLock) {
int size = mFrozenProcesses.size();
pw.println(" Apps frozen: " + size);
@@ -1308,10 +1340,22 @@
mFreezerBinderThreshold = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_BINDER_THRESHOLD, DEFAULT_FREEZER_BINDER_THRESHOLD);
+ mFreezerBinderCallbackEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_FREEZER_BINDER_CALLBACK_ENABLED, DEFAULT_FREEZER_BINDER_CALLBACK_ENABLED);
+ mFreezerBinderCallbackThrottle = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_FREEZER_BINDER_CALLBACK_THROTTLE, DEFAULT_FREEZER_BINDER_CALLBACK_THROTTLE);
+ mFreezerBinderAsyncThreshold = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+ KEY_FREEZER_BINDER_ASYNC_THRESHOLD, DEFAULT_FREEZER_BINDER_ASYNC_THRESHOLD);
Slog.d(TAG_AM, "Freezer binder state set to enabled=" + mFreezerBinderEnabled
+ ", divisor=" + mFreezerBinderDivisor
+ ", offset=" + mFreezerBinderOffset
- + ", threshold=" + mFreezerBinderThreshold);
+ + ", threshold=" + mFreezerBinderThreshold
+ + ", callback enabled=" + mFreezerBinderCallbackEnabled
+ + ", callback throttle=" + mFreezerBinderCallbackThrottle
+ + ", async threshold=" + mFreezerBinderAsyncThreshold);
}
private boolean parseProcStateThrottle(String procStateThrottleString) {
@@ -2174,6 +2218,21 @@
Slog.w(TAG_AM, "Unable to check file locks");
}
} break;
+ case BINDER_ERROR_MSG: {
+ IntArray pids = new IntArray();
+ // Copy the frozen pids to a local array to release mProcLock ASAP
+ synchronized (mProcLock) {
+ int size = mFrozenProcesses.size();
+ for (int i = 0; i < size; i++) {
+ pids.add(mFrozenProcesses.keyAt(i));
+ }
+ }
+
+ // Check binder errors to frozen processes with a local freezer lock
+ synchronized (mFreezerLock) {
+ binderErrorLocked(pids);
+ }
+ } break;
default:
return;
}
@@ -2479,4 +2538,115 @@
return UNFREEZE_REASON_NONE;
}
}
+
+ /**
+ * Kill a frozen process with a specified reason
+ */
+ public void killProcess(int pid, String reason, @Reason int reasonCode,
+ @SubReason int subReason) {
+ mAm.mHandler.post(() -> {
+ synchronized (mAm) {
+ synchronized (mProcLock) {
+ ProcessRecord proc = mFrozenProcesses.get(pid);
+ // The process might have been killed or unfrozen by others
+ if (proc != null && proc.getThread() != null && !proc.isKilledByAm()) {
+ proc.killLocked(reason, reasonCode, subReason, true);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Sending binder transactions to frozen apps most likely indicates there's a bug. Log it and
+ * kill the frozen apps if they 1) receive sync binder transactions while frozen, or 2) miss
+ * async binder transactions due to kernel binder buffer running out.
+ *
+ * @param debugPid The binder transaction sender
+ * @param app The ProcessRecord of the sender
+ * @param code The binder transaction code
+ * @param flags The binder transaction flags
+ * @param err The binder transaction error
+ */
+ public void binderError(int debugPid, ProcessRecord app, int code, int flags, int err) {
+ Slog.w(TAG_AM, "pid " + debugPid + " " + (app == null ? "null" : app.processName)
+ + " sent binder code " + code + " with flags " + flags
+ + " to frozen apps and got error " + err);
+
+ // Do nothing if the binder error callback is not enabled.
+ // That means the frozen apps in a wrong state will be killed when they are unfrozen later.
+ if (!mUseFreezer || !mFreezerBinderCallbackEnabled) {
+ return;
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ if (now < mFreezerBinderCallbackLast + mFreezerBinderCallbackThrottle) {
+ Slog.d(TAG_AM, "Too many transaction errors, throttling freezer binder callback.");
+ return;
+ }
+ mFreezerBinderCallbackLast = now;
+
+ // Check all frozen processes in Freezer handler
+ mFreezeHandler.sendEmptyMessage(BINDER_ERROR_MSG);
+ }
+
+ private void binderErrorLocked(IntArray pids) {
+ // PIDs that run out of async binder buffer when being frozen
+ ArraySet<Integer> pidsAsync = (mFreezerBinderAsyncThreshold < 0) ? null : new ArraySet<>();
+
+ for (int i = 0; i < pids.size(); i++) {
+ int current = pids.get(i);
+ try {
+ int freezeInfo = getBinderFreezeInfo(current);
+
+ if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ killProcess(current, "Sync transaction while frozen",
+ ApplicationExitInfo.REASON_FREEZER,
+ ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION);
+
+ // No need to check async transactions in this case
+ continue;
+ }
+
+ if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
+ if (pidsAsync != null) {
+ pidsAsync.add(current);
+ }
+ if (DEBUG_FREEZER) {
+ Slog.w(TAG_AM, "pid " + current
+ + " received async transactions while frozen");
+ }
+ }
+ } catch (Exception e) {
+ // The process has died. No need to kill it again.
+ Slog.w(TAG_AM, "Unable to query binder frozen stats for pid " + current);
+ }
+ }
+
+ // TODO: when kernel binder driver supports, poll the binder status directly.
+ // Binderfs stats, like other debugfs files, is not a reliable interface. But it's the
+ // only true source for now. The following code checks all frozen PIDs. If any of them
+ // is running out of async binder buffer, kill it. Otherwise it will be killed at a
+ // later time when AMS unfreezes it, which causes race issues.
+ if (pidsAsync == null || pidsAsync.size() == 0) {
+ return;
+ }
+ new BinderfsStatsReader().handleFreeAsyncSpace(
+ // Check if the frozen process has pending async calls
+ pidsAsync::contains,
+
+ // Kill the current process if it's running out of async binder space
+ (current, free) -> {
+ if (free < mFreezerBinderAsyncThreshold) {
+ Slog.w(TAG_AM, "pid " + current
+ + " has " + free + " free async space, killing");
+ killProcess(current, "Async binder space running out while frozen",
+ ApplicationExitInfo.REASON_FREEZER,
+ ApplicationExitInfo.SUBREASON_FREEZER_BINDER_ASYNC_FULL);
+ }
+ },
+
+ // Log the error if binderfs stats can't be accesses or correctly parsed
+ exception -> Slog.e(TAG_AM, "Unable to parse binderfs stats"));
+ }
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d2929ae..a959fc1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3706,7 +3706,8 @@
if (type == XmlPullParser.START_TAG) {
final String name = parser.getName();
if (name.equals(TAG_USER)) {
- UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+ UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID),
+ mUserVersion);
if (userData != null) {
synchronized (mUsersLock) {
@@ -4387,7 +4388,7 @@
}
@GuardedBy({"mPackagesLock"})
- private UserData readUserLP(int id) {
+ private UserData readUserLP(int id, int userVersion) {
try (ResilientAtomicFile file = getUserFile(id)) {
FileInputStream fis = null;
try {
@@ -4396,19 +4397,19 @@
Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
return null;
}
- return readUserLP(id, fis);
+ return readUserLP(id, fis, userVersion);
} catch (Exception e) {
// Remove corrupted file and retry.
Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
file.failRead(fis, e);
- return readUserLP(id);
+ return readUserLP(id, userVersion);
}
}
}
@GuardedBy({"mPackagesLock"})
@VisibleForTesting
- UserData readUserLP(int id, InputStream is) throws IOException,
+ UserData readUserLP(int id, InputStream is, int userVersion) throws IOException,
XmlPullParserException {
int flags = 0;
String userType = null;
@@ -4501,7 +4502,17 @@
} else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) {
legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) {
- localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ if (userVersion < 10) {
+ // Prior to version 10, the local user restrictions were stored as sub tags
+ // grouped by the user id of the source user. The source is no longer stored
+ // on versions 10+ as this is now stored in the DevicePolicyEngine.
+ RestrictionsSet oldLocalRestrictions =
+ RestrictionsSet.readRestrictions(
+ parser, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS);
+ localRestrictions = oldLocalRestrictions.mergeAll();
+ } else {
+ localRestrictions = UserRestrictionsUtils.readRestrictions(parser);
+ }
} else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) {
globalRestrictions = UserRestrictionsUtils.readRestrictions(parser);
} else if (TAG_ACCOUNT.equals(tag)) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5f1df78..cca92aa 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -65,6 +65,7 @@
import android.os.FactoryTest;
import android.os.FileUtils;
import android.os.IBinder;
+import android.os.IBinderCallback;
import android.os.IIncidentManager;
import android.os.Looper;
import android.os.Message;
@@ -980,6 +981,14 @@
}
}
+ // Set binder transaction callback after starting system services
+ Binder.setTransactionCallback(new IBinderCallback() {
+ @Override
+ public void onTransactionError(int pid, int code, int flags, int err) {
+ mActivityManagerService.frozenBinderTransactionDetected(pid, code, flags, err);
+ }
+ });
+
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
diff --git a/services/tests/servicestests/res/xml/user_100_v9.xml b/services/tests/servicestests/res/xml/user_100_v9.xml
new file mode 100644
index 0000000..03c08ed
--- /dev/null
+++ b/services/tests/servicestests/res/xml/user_100_v9.xml
@@ -0,0 +1,20 @@
+<user id="100"
+ serialNumber="0"
+ flags="3091"
+ type="android.os.usertype.full.SYSTEM"
+ created="0"
+ lastLoggedIn="0"
+ lastLoggedInFingerprint="0"
+ profileBadge="0">
+ <restrictions no_oem_unlock="true" />
+ <device_policy_local_restrictions>
+ <restrictions_user user_id="0">
+ <restrictions no_camera="true" />
+ </restrictions_user>
+ <restrictions_user user_id="100">
+ <restrictions no_camera="true" />
+ <restrictions no_install_unknown_sources="true" />
+ </restrictions_user>
+ </device_policy_local_restrictions>
+ <ignorePrepareStorageErrors>false</ignorePrepareStorageErrors>
+</user>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 9f75cf8..429c58e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -43,27 +43,33 @@
import android.app.PropertyInvalidatedCache;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Resources;
import android.os.Looper;
import android.os.Parcel;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.text.TextUtils;
+import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.frameworks.servicestests.R;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerService.UserData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -76,6 +82,7 @@
@MediumTest
public class UserManagerServiceUserInfoTest {
private UserManagerService mUserManagerService;
+ private Resources mResources;
@Before
public void setup() {
@@ -95,6 +102,8 @@
assertEquals("Multiple users so this test can't run.", 1, users.size());
assertEquals("Only user present isn't the system user.",
UserHandle.USER_SYSTEM, users.get(0).id);
+
+ mResources = InstrumentationRegistry.getTargetContext().getResources();
}
@Test
@@ -108,7 +117,7 @@
byte[] bytes = baos.toByteArray();
UserData read = mUserManagerService.readUserLP(
- data.info.id, new ByteArrayInputStream(bytes));
+ data.info.id, new ByteArrayInputStream(bytes), 0);
assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false);
}
@@ -135,7 +144,11 @@
// Clear the restrictions to see if they are properly read in from the user file.
setUserRestrictions(data.info.id, globalRestriction, localRestriction, false);
- mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes));
+ final int userVersion = 10;
+ //read the secondary and SYSTEM user file to fetch local/global device policy restrictions.
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(bytes),
+ userVersion);
+
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction));
assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(localRestriction));
}
@@ -286,6 +299,45 @@
assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO));
}
+ /** Tests readUserLP upgrading from version 9 to 10+. */
+ @Test
+ public void testUserRestrictionsUpgradeFromV9() throws Exception {
+ final String[] localRestrictions = new String[] {
+ UserManager.DISALLOW_CAMERA,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ };
+
+ final int userId = 100;
+ UserData data = new UserData();
+ data.info = createUser(userId, FLAG_FULL, "A type");
+
+ mUserManagerService.putUserInfo(data.info);
+
+ for (String restriction : localRestrictions) {
+ assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
+ assertFalse(mUserManagerService.hasUserRestriction(restriction, userId));
+ }
+
+ // Convert the xml resource to the system storage xml format.
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ DataOutputStream os = new DataOutputStream(baos);
+ XmlPullParser in = mResources.getXml(R.xml.user_100_v9);
+ XmlSerializer out = Xml.newBinarySerializer();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+ Xml.copy(in, out);
+ byte[] userBytes = baos.toByteArray();
+ baos.reset();
+
+ final int userVersion = 9;
+ mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(userBytes),
+ userVersion);
+
+ for (String restriction : localRestrictions) {
+ assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId));
+ assertTrue(mUserManagerService.hasUserRestriction(restriction, userId));
+ }
+ }
+
/** Creates a UserInfo with the given flags and userType. */
private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) {
return new UserInfo(userId, "A Name", "A path", flags, userType);