Merge "Fix lint errors around import lines in IInputConnectionWrapper"
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 66a2600d..d8d4a6e 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -23,8 +23,7 @@
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal.CallSession;
-
-import java.util.Random;
+import com.android.internal.os.CachedDeviceState;
import org.junit.After;
import org.junit.Before;
@@ -32,8 +31,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertNull;
-
/**
* Performance tests for {@link BinderCallsStats}
@@ -49,6 +46,8 @@
@Before
public void setUp() {
mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+ CachedDeviceState deviceState = new CachedDeviceState(false, false);
+ mBinderCallsStats.setDeviceState(deviceState.getReadonlyClient());
}
@After
diff --git a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
new file mode 100644
index 0000000..0f880b7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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;
+
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Performance tests for {@link LooperStats}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LooperStatsPerfTest {
+ private static final int DISTINCT_MESSAGE_COUNT = 1000;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private LooperStats mStats;
+ private CachedDeviceState mDeviceState;
+ private HandlerThread mThread;
+ private Message[] mMessages = new Message[DISTINCT_MESSAGE_COUNT];
+
+ @Before
+ public void setUp() {
+ mStats = new LooperStats(1, DISTINCT_MESSAGE_COUNT - 1);
+ mDeviceState = new CachedDeviceState(false, false);
+ mStats.setDeviceState(mDeviceState.getReadonlyClient());
+ // The tests are all single-threaded. HandlerThread is created to allow creating Handlers.
+ mThread = new HandlerThread("UnusedThread");
+ mThread.start();
+ for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+ mMessages[i] = mThread.getThreadHandler().obtainMessage(i);
+ }
+ }
+
+ @After
+ public void tearDown() {
+ mThread.quit();
+ }
+
+ @Test
+ public void timeHundredPercentSampling() {
+ mStats.setSamplingInterval(1);
+ runScenario();
+ }
+
+ @Test
+ public void timeOnePercentSampling() {
+ mStats.setSamplingInterval(100);
+ runScenario();
+ }
+
+ @Test
+ public void timeCollectionDisabled() {
+ // We do not collect data on charger.
+ mDeviceState.setCharging(true);
+ runScenario();
+ }
+
+ private void runScenario() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+ Object token = mStats.messageDispatchStarting();
+ mStats.messageDispatched(token, mMessages[i]);
+ }
+ }
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index a3b5c7aaea..6ad1663 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -11313,6 +11313,7 @@
field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
+ field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -11322,6 +11323,7 @@
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+ field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -13984,6 +13986,8 @@
method public float getRunAdvance(char[], int, int, int, int, boolean, int);
method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
method public android.graphics.Shader getShader();
+ method public float getStrikeThruPosition();
+ method public float getStrikeThruThickness();
method public android.graphics.Paint.Cap getStrokeCap();
method public android.graphics.Paint.Join getStrokeJoin();
method public float getStrokeMiter();
@@ -14004,6 +14008,9 @@
method public int getTextWidths(java.lang.String, int, int, float[]);
method public int getTextWidths(java.lang.String, float[]);
method public android.graphics.Typeface getTypeface();
+ method public float getUnderlinePosition();
+ method public float getUnderlineThickness();
+ method public float getWordSpacing();
method public android.graphics.Xfermode getXfermode();
method public boolean hasGlyph(java.lang.String);
method public final boolean isAntiAlias();
@@ -14055,6 +14062,7 @@
method public void setTextSkewX(float);
method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
method public void setUnderlineText(boolean);
+ method public void setWordSpacing(float);
method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
@@ -15135,9 +15143,9 @@
public class StateListDrawable extends android.graphics.drawable.DrawableContainer {
ctor public StateListDrawable();
method public void addState(int[], android.graphics.drawable.Drawable);
+ method public int findStateDrawableIndex(int[]);
method public int getStateCount();
method public android.graphics.drawable.Drawable getStateDrawable(int);
- method public int getStateDrawableIndex(int[]);
method public int[] getStateSet(int);
}
@@ -45984,6 +45992,7 @@
method public float getDimension(android.util.DisplayMetrics);
method public final float getFloat();
method public float getFraction(float, float);
+ method public boolean isColorType();
method public void setTo(android.util.TypedValue);
field public static final int COMPLEX_MANTISSA_MASK = 16777215; // 0xffffff
field public static final int COMPLEX_MANTISSA_SHIFT = 8; // 0x8
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3c9f7ee..2ecfbe7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -133,7 +133,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10024
+ // Next: 10025
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -159,6 +159,7 @@
Temperature temperature = 10021;
BinderCalls binder_calls = 10022;
BinderCallsExceptions binder_calls_exceptions = 10023;
+ LooperStats looper_stats = 10024;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -2203,3 +2204,50 @@
// Total number of exceptions.
optional int64 exception_count = 2;
}
+
+message LooperStats {
+ // Currently not collected and always set to 0.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Fully qualified class name of the handler target class.
+ //
+ // This field does not contain PII. This is a system server class name.
+ optional string handler_class_name = 2;
+
+ // The name of the thread that runs the Looper.
+ //
+ // This field does not contain PII. This is a system server thread name.
+ optional string looper_thread_name = 3;
+
+ // The name of the dispatched message.
+ //
+ // This field does not contain PII. This is a system server constant or class
+ // name.
+ optional string message_name = 4;
+
+ // Total number of successfully dispatched messages.
+ optional int64 message_count = 5;
+
+ // Total number of messages that failed dispatching.
+ optional int64 exception_count = 6;
+
+ // Total number of processed messages we have data recorded for. If we
+ // collected data for all the messages, message_count will be equal to
+ // recorded_message_count.
+ //
+ // If recorded_message_count is different than message_count, it means data
+ // collection has been sampled. All the fields below will be sampled in this
+ // case.
+ optional int64 recorded_message_count = 7;
+
+ // Total latency of all processed messages.
+ // Average can be computed using recorded_total_latency_micros /
+ // recorded_message_count.
+ optional int64 recorded_total_latency_micros = 8;
+
+ // Total CPU usage of all processed message.
+ // Average can be computed using recorded_total_cpu_micros /
+ // recorded_message_count. Total can be computed using
+ // recorded_total_cpu_micros / recorded_message_count * call_count.
+ optional int64 recorded_total_cpu_micros = 9;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index e44351b..42cc543 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -105,9 +105,9 @@
readPipe.reset(); // Close the read end (owned by the child process).
- // Using fopen() because fwrite() has the right logic to chunking write()
+ // Using fdopen() because fwrite() has the right logic to chunking write()
// over a pipe (see __sfvwrite()).
- FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+ FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb");
if (!writePipeStream) {
ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
return false;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index e6e8455..2871882 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -183,7 +183,13 @@
{{},
{},
1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}
+ new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+ // looper_stats
+ {android::util::LOOPER_STATS,
+ {{5, 6, 7, 8, 9},
+ {2, 3, 4},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::LOOPER_STATS)}}
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 02719e3f8..45e38cf 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -152,8 +152,8 @@
Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;Landroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;)Z
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
Landroid/view/IWindowManager;->endProlongedAnimations()V
Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1108f93..225b6cf 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -615,6 +615,13 @@
public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
/**
+ * Value for {@link #privateFlags}: whether this app is signed with the
+ * platform key.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
+ /**
* Value for {@link #privateFlags}: whether this app is pre-installed on the
* google partition of the system image.
* @hide
@@ -622,11 +629,11 @@
public static final int PRIVATE_FLAG_PRODUCT_SERVICES = 1 << 21;
/**
- * Value for {@link #privateFlags}: whether this app is signed with the
- * platform key.
+ * Indicates whether this package requires access to non-SDK APIs.
+ * Only system apps and tests are allowed to use this property.
* @hide
*/
- public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+ public static final int PRIVATE_FLAG_USES_NON_SDK_API = 1 << 22;
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -1009,13 +1016,6 @@
public String appComponentFactory;
/**
- * Indicates whether this package requires access to non-SDK APIs. Only system apps
- * and tests are allowed to use this property.
- * @hide
- */
- public boolean usesNonSdkApi;
-
- /**
* The category of this app. Categories are used to cluster multiple apps
* together into meaningful groups, such as when summarizing battery,
* network, or disk usage. Apps should only define this value when they fit
@@ -1294,6 +1294,7 @@
pw.println(prefix + "category=" + category);
}
pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
+ pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
}
super.dumpBack(pw, prefix);
}
@@ -1718,11 +1719,18 @@
return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
}
+ /**
+ * @hide
+ */
+ public boolean usesNonSdkApi() {
+ return (privateFlags & PRIVATE_FLAG_USES_NON_SDK_API) != 0;
+ }
+
private boolean isAllowedToUseHiddenApis() {
if (isSignedWithPlatformKey()) {
return true;
} else if (isSystemApp() || isUpdatedSystemApp()) {
- return usesNonSdkApi || isPackageWhitelistedForHiddenApis();
+ return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
} else {
return false;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7d8ff4c..5b0e856 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2274,7 +2274,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FACE = "android.hardware.face";
@@ -2282,7 +2281,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_IRIS = "android.hardware.iris";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8b058dc..6d49362 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3659,8 +3659,10 @@
ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
}
- ai.usesNonSdkApi = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false);
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
+ }
if (outError[0] == null) {
CharSequence pname;
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index b4955ea..496f34c 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -30,6 +30,12 @@
* @hide Only for use within the system server.
*/
public final class DisplayViewport {
+
+ // Viewport constants defined in InputReader.h.
+ public static final int VIEWPORT_INTERNAL = 1;
+ public static final int VIEWPORT_EXTERNAL = 2;
+ public static final int VIEWPORT_VIRTUAL = 3;
+
// True if this viewport is valid.
public boolean valid;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3141be4..ae0855a 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -300,6 +300,23 @@
public static final String EXTRA_PERMISSION_GRANTED = "permission";
/**
+ * Name of extra added to start systemui.usb.UsbPermissionActivity
+ * containing package name of the app which requests USB permission.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE";
+
+ /**
+ * Name of extra added to start systemui.usb.UsbPermissionActivity
+ * containing the whether the app which requests USB permission can be set as default handler
+ * for USB device attach event or USB accessory attach event or not.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT";
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9295bb7..64c934b 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -450,10 +450,12 @@
@MainThread
@Override
public void attachToken(IBinder token) {
- if (mToken == null) {
- mToken = token;
- mWindow.setToken(token);
+ if (mToken != null) {
+ throw new IllegalStateException(
+ "attachToken() must be called at most once. token=" + token);
}
+ mToken = token;
+ mWindow.setToken(token);
}
/**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 684a8ee..ded3a19 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.LooperProto;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
@@ -70,6 +69,7 @@
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
+ private static Observer sObserver;
final MessageQueue mQueue;
final Thread mThread;
@@ -131,6 +131,15 @@
}
/**
+ * Set the transaction observer for all Loopers in this process.
+ *
+ * @hide
+ */
+ public static void setObserver(@Nullable Observer observer) {
+ sObserver = observer;
+ }
+
+ /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@@ -169,6 +178,8 @@
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
+ // Make sure the observer won't change while processing a transaction.
+ final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
@@ -189,9 +200,21 @@
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
+ Object token = null;
+ if (observer != null) {
+ token = observer.messageDispatchStarting();
+ }
try {
msg.target.dispatchMessage(msg);
+ if (observer != null) {
+ observer.messageDispatched(token, msg);
+ }
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
+ } catch (Exception exception) {
+ if (observer != null) {
+ observer.dispatchingThrewException(token, msg, exception);
+ }
+ throw exception;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
@@ -397,4 +420,39 @@
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
+
+ /** {@hide} */
+ public interface Observer {
+ /**
+ * Called right before a message is dispatched.
+ *
+ * <p> The token type is not specified to allow the implementation to specify its own type.
+ *
+ * @return a token used for collecting telemetry when dispatching a single message.
+ * The token token must be passed back exactly once to either
+ * {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
+ * and must not be reused again.
+ *
+ */
+ Object messageDispatchStarting();
+
+ /**
+ * Called when a message was processed by a Handler.
+ *
+ * @param token Token obtained by previously calling
+ * {@link Observer#messageDispatchStarting} on the same Observer instance.
+ * @param msg The message that was dispatched.
+ */
+ void messageDispatched(Object token, Message msg);
+
+ /**
+ * Called when an exception was thrown while processing a message.
+ *
+ * @param token Token obtained by previously calling
+ * {@link Observer#messageDispatchStarting} on the same Observer instance.
+ * @param msg The message that was dispatched and caused an exception.
+ * @param exception The exception that was thrown.
+ */
+ void dispatchingThrewException(Object token, Message msg, Exception exception);
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1cc67d..e007398 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7256,6 +7256,14 @@
private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * Gesture that wakes up the display, showing the ambient version of the status bar.
+ * @hide
+ */
+ public static final String DOZE_WAKE_SCREEN_GESTURE = "doze_wake_screen_gesture";
+
+ private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -8158,6 +8166,7 @@
DOZE_PICK_UP_GESTURE,
DOZE_DOUBLE_TAP_GESTURE,
DOZE_REACH_GESTURE,
+ DOZE_WAKE_SCREEN_GESTURE,
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -8302,6 +8311,7 @@
VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+ VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
@@ -13308,6 +13318,36 @@
* @hide
*/
public static final String BINDER_CALLS_STATS = "binder_calls_stats";
+
+ /**
+ * Looper stats settings.
+ *
+ * The following strings are supported as keys:
+ * <pre>
+ * enabled (boolean)
+ * sampling_interval (int)
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String LOOPER_STATS = "looper_stats";
+
+ /**
+ * Default user id to boot into. They map to user ids, for example, 10, 11, 12.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_USER_ID_TO_BOOT_INTO = "default_boot_into_user_id";
+
+ /**
+ * Persistent user id that is last logged in to.
+ *
+ * They map to user ids, for example, 10, 11, 12.
+ *
+ * @hide
+ */
+ public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
+
}
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index febdb83..4bd86a4 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -164,8 +164,7 @@
int mType;
int mCurWidth;
int mCurHeight;
- int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_SCALED;
+ int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
int mWindowPrivateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
@@ -776,6 +775,7 @@
mDisplay.getDisplayInfo(displayInfo);
mLayout.width = Math.max(displayInfo.logicalWidth, myWidth);
mLayout.height = Math.max(displayInfo.logicalHeight, myHeight);
+ mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED;
}
mLayout.format = mFormat;
@@ -785,8 +785,7 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SCALED;
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mCurWindowPrivateFlags = mWindowPrivateFlags;
mLayout.privateFlags = mWindowPrivateFlags;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index a7f14b6..b5ade2a 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,6 +45,7 @@
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
}
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 2c9bb66..9640173 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -232,6 +232,16 @@
};
/**
+ * Determine if a value is a color by comparing {@link type} to {@link #TYPE_FIRST_COLOR_INT}
+ * and {@link #TYPE_LAST_COLOR_INT}.
+ *
+ * @return true if this value is a color
+ */
+ public boolean isColorType() {
+ return (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT);
+ }
+
+ /**
* Retrieve the base value from a complex data integer. This uses the
* {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of
* the data to compute a floating point representation of the number they
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c205af0..bfe1e95 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -391,15 +391,16 @@
void registerShortcutKey(in long shortcutCode, IShortcutService keySubscriber);
/**
- * Create an input consumer by name.
+ * Create an input consumer by name and display id.
*/
- void createInputConsumer(IBinder token, String name, out InputChannel inputChannel);
+ void createInputConsumer(IBinder token, String name, int displayId,
+ out InputChannel inputChannel);
/**
- * Destroy an input consumer by name. This method will also dispose the input channels
- * associated with that InputConsumer.
+ * Destroy an input consumer by name and display id.
+ * This method will also dispose the input channels associated with that InputConsumer.
*/
- boolean destroyInputConsumer(String name);
+ boolean destroyInputConsumer(String name, int displayId);
/**
* Return the touch region for the current IME window, or an empty region if there is none.
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index ab8886b..ca14ebe 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -86,10 +86,10 @@
* needed to identify itself with the service to validate its operations.
* This token <strong>must not</strong> be passed to applications, since
* it grants special priviledges that should not be given to applications.
- *
- * <p>Note: to protect yourself from malicious clients, you should only
- * accept the first token given to you. Any after that may come from the
- * client.
+ *
+ * <p>The system guarantees that this method is called back between
+ * {@link InputMethodService#onCreate()} and {@link InputMethodService#onDestroy()}
+ * at most once.
*/
@MainThread
public void attachToken(IBinder token);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d3fd0cf..0ecd09f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2333,7 +2333,13 @@
}
/**
- * @return The current height of the input method window.
+ * This is kept due to {@link android.annotation.UnsupportedAppUsage}.
+ *
+ * <p>TODO(Bug 113914148): Check if we can remove this. We have accidentally exposed
+ * WindowManagerInternal#getInputMethodWindowVisibleHeight to app developers and some of them
+ * started relying on it.</p>
+ *
+ * @return Something that is not well-defined.
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0da47fd..1883809 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -545,7 +545,7 @@
StateListDrawable stateList = (StateListDrawable) mBackground;
// Find the above-anchor view - this one's easy, it should be labeled as such.
- int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
+ int aboveAnchorStateIndex = stateList.findStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
// Now, for the below-anchor view, look for any other drawable specified in the
// StateListDrawable which is not for the above-anchor state and use that.
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 8728367..cb282b6 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -75,6 +75,15 @@
return !TextUtils.isEmpty(reachSensorType());
}
+ public boolean wakeScreenGestureEnabled(int user) {
+ return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user)
+ && wakeScreenGestureAvailable();
+ }
+
+ public boolean wakeScreenGestureAvailable() {
+ return !TextUtils.isEmpty(wakeScreenSensorType());
+ }
+
public String doubleTapSensorType() {
return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
}
@@ -87,6 +96,10 @@
return mContext.getResources().getString(R.string.config_dozeReachSensorType);
}
+ public String wakeScreenSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeWakeScreenSensorType);
+ }
+
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 4aa30f6..c0c358d 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,16 +16,9 @@
package com.android.internal.os;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
import android.os.Binder;
-import android.os.OsProtoEnums;
-import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
@@ -37,7 +30,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderInternal.CallSession;
-import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
@@ -63,7 +55,6 @@
private static final String TAG = "BinderCallsStats";
private static final int CALL_SESSIONS_POOL_SIZE = 100;
- private static final int PERIODIC_SAMPLING_INTERVAL = 10;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
@@ -81,25 +72,7 @@
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
- // State updated by the broadcast receiver below.
- private boolean mScreenInteractive;
- private boolean mCharging;
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case Intent.ACTION_BATTERY_CHANGED:
- mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
- break;
- case Intent.ACTION_SCREEN_ON:
- mScreenInteractive = true;
- break;
- case Intent.ACTION_SCREEN_OFF:
- mScreenInteractive = false;
- break;
- }
- }
- };
+ private CachedDeviceState.Readonly mDeviceState;
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
@@ -112,65 +85,14 @@
this.mRandom = injector.getRandomGenerator();
}
- public void systemReady(Context context) {
- registerBroadcastReceiver(context);
- setInitialState(queryScreenInteractive(context), queryIsCharging());
- }
-
- /**
- * Listens for screen/battery state changes.
- */
- @VisibleForTesting
- public void registerBroadcastReceiver(Context context) {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- /**
- * Sets the battery/screen initial state.
- *
- * This has to be updated *after* the broadcast receiver is installed.
- */
- @VisibleForTesting
- public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
- this.mScreenInteractive = isScreenInteractive;
- this.mCharging = isCharging;
- // Data collected previously was not accurate since the battery/screen state was not set.
- reset();
- }
-
- private boolean queryIsCharging() {
- final BatteryManagerInternal batteryManager =
- LocalServices.getService(BatteryManagerInternal.class);
- if (batteryManager == null) {
- Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
- // Default to true to not collect any data.
- return true;
- } else {
- return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
- }
- }
-
- private boolean queryScreenInteractive(Context context) {
- final PowerManager powerManager = context.getSystemService(PowerManager.class);
- final boolean screenInteractive;
- if (powerManager == null) {
- Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
- new Throwable());
- return true;
- } else {
- return powerManager.isInteractive();
- }
+ public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ mDeviceState = deviceState;
}
@Override
@Nullable
public CallSession callStarted(Binder binder, int code) {
- if (mCharging) {
+ if (mDeviceState == null || mDeviceState.isCharging()) {
return null;
}
@@ -221,7 +143,7 @@
synchronized (mLock) {
// This was already checked in #callStart but check again while synchronized.
- if (mCharging) {
+ if (mDeviceState == null || mDeviceState.isCharging()) {
return;
}
@@ -233,7 +155,7 @@
uidEntry.recordedCallCount++;
final CallStat callStat = uidEntry.getOrCreate(
- s.binderClass, s.transactionCode, mScreenInteractive);
+ s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
callStat.callCount++;
callStat.recordedCallCount++;
callStat.cpuTimeMicros += duration;
@@ -252,7 +174,7 @@
// Only record the total call count if we already track data for this key.
// It helps to keep the memory usage down when sampling is enabled.
final CallStat callStat = uidEntry.get(
- s.binderClass, s.transactionCode, mScreenInteractive);
+ s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
if (callStat != null) {
callStat.callCount++;
}
@@ -319,13 +241,13 @@
public ArrayList<ExportedCallStat> getExportedCallStats() {
// We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) {
- return new ArrayList<ExportedCallStat>();
+ return new ArrayList<>();
}
ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
synchronized (mLock) {
final int uidEntriesSize = mUidEntries.size();
- for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++){
+ for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
final UidEntry entry = mUidEntries.valueAt(entryIdx);
for (CallStat stat : entry.getCallStatsList()) {
ExportedCallStat exported = new ExportedCallStat();
@@ -387,13 +309,15 @@
}
}
- public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+ /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
+ public void dump(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap, boolean verbose) {
synchronized (mLock) {
dumpLocked(pw, appIdToPkgNameMap, verbose);
}
}
- private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+ private void dumpLocked(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap,
+ boolean verbose) {
long totalCallsCount = 0;
long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
@@ -450,13 +374,13 @@
for (UidEntry entry : summaryEntries) {
String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
- entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
- entry.recordedCallCount, entry.callCount, uidStr));
+ entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+ entry.recordedCallCount, entry.callCount, uidStr));
}
pw.println();
pw.println(String.format(" Summary: total_cpu_time=%d, "
- + "calls_count=%d, avg_call_cpu_time=%.0f",
- totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
+ + "calls_count=%d, avg_call_cpu_time=%.0f",
+ totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
pw.println();
pw.println("Exceptions thrown (exception_count, class_name):");
@@ -723,11 +647,6 @@
return result;
}
- @VisibleForTesting
- public BroadcastReceiver getBroadcastReceiver() {
- return mBroadcastReceiver;
- }
-
private static int compareByCpuDesc(
ExportedCallStat a, ExportedCallStat b) {
return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
new file mode 100644
index 0000000..8c90682
--- /dev/null
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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.annotations.VisibleForTesting;
+
+/**
+ * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
+ * the System Server telemetry services.
+ *
+ * @hide
+ */
+public class CachedDeviceState {
+ private volatile boolean mScreenInteractive;
+ private volatile boolean mCharging;
+
+ public CachedDeviceState() {
+ mCharging = true;
+ mScreenInteractive = false;
+ }
+
+ @VisibleForTesting
+ public CachedDeviceState(boolean isCharging, boolean isScreenInteractive) {
+ mCharging = isCharging;
+ mScreenInteractive = isScreenInteractive;
+ }
+
+ public void setScreenInteractive(boolean screenInteractive) {
+ mScreenInteractive = screenInteractive;
+ }
+
+ public void setCharging(boolean charging) {
+ mCharging = charging;
+ }
+
+ public Readonly getReadonlyClient() {
+ return new CachedDeviceState.Readonly();
+ }
+
+ /**
+ * Allows for only a readonly access to the device state.
+ */
+ public class Readonly {
+ public boolean isCharging() {
+ return mCharging;
+ }
+
+ public boolean isScreenInteractive() {
+ return mScreenInteractive;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
new file mode 100644
index 0000000..02a8b22
--- /dev/null
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2018 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 android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Collects aggregated telemetry data about Looper message dispatching.
+ *
+ * @hide Only for use within the system server.
+ */
+public class LooperStats implements Looper.Observer {
+ private static final int TOKEN_POOL_SIZE = 50;
+
+ @GuardedBy("mLock")
+ private final SparseArray<Entry> mEntries = new SparseArray<>(512);
+ private final Object mLock = new Object();
+ private final Entry mOverflowEntry = new Entry("OVERFLOW");
+ private final Entry mHashCollisionEntry = new Entry("HASH_COLLISION");
+ private final ConcurrentLinkedQueue<DispatchSession> mSessionPool =
+ new ConcurrentLinkedQueue<>();
+ private final int mEntriesSizeCap;
+ private int mSamplingInterval;
+ private CachedDeviceState.Readonly mDeviceState;
+
+ public LooperStats(int samplingInterval, int entriesSizeCap) {
+ this.mSamplingInterval = samplingInterval;
+ this.mEntriesSizeCap = entriesSizeCap;
+ }
+
+ public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ mDeviceState = deviceState;
+ }
+
+ @Override
+ public Object messageDispatchStarting() {
+ if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
+ DispatchSession session = mSessionPool.poll();
+ session = session == null ? new DispatchSession() : session;
+ session.startTimeMicro = getElapsedRealtimeMicro();
+ session.cpuStartMicro = getThreadTimeMicro();
+ return session;
+ }
+
+ return DispatchSession.NOT_SAMPLED;
+ }
+
+ @Override
+ public void messageDispatched(Object token, Message msg) {
+ if (!deviceStateAllowsCollection()) {
+ return;
+ }
+
+ DispatchSession session = (DispatchSession) token;
+ Entry entry = getOrCreateEntry(msg);
+ synchronized (entry) {
+ entry.messageCount++;
+ if (session != DispatchSession.NOT_SAMPLED) {
+ entry.recordedMessageCount++;
+ long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+ long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+ entry.totalLatencyMicro += latency;
+ entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+ entry.cpuUsageMicro += cpuUsage;
+ entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ }
+ }
+
+ recycleSession(session);
+ }
+
+ @Override
+ public void dispatchingThrewException(Object token, Message msg, Exception exception) {
+ if (!deviceStateAllowsCollection()) {
+ return;
+ }
+
+ DispatchSession session = (DispatchSession) token;
+ Entry entry = getOrCreateEntry(msg);
+ synchronized (entry) {
+ entry.exceptionCount++;
+ }
+ recycleSession(session);
+ }
+
+ private boolean deviceStateAllowsCollection() {
+ // Do not collect data if on charger or the state is not set.
+ return mDeviceState != null && !mDeviceState.isCharging();
+ }
+
+ /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
+ public List<ExportedEntry> getEntries() {
+ final ArrayList<ExportedEntry> entries;
+ synchronized (mLock) {
+ final int size = mEntries.size();
+ entries = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Entry entry = mEntries.valueAt(i);
+ synchronized (entry) {
+ entries.add(new ExportedEntry(entry));
+ }
+ }
+ }
+ // Add the overflow and collision entries only if they have any data.
+ if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) {
+ synchronized (mOverflowEntry) {
+ entries.add(new ExportedEntry(mOverflowEntry));
+ }
+ }
+ if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) {
+ synchronized (mHashCollisionEntry) {
+ entries.add(new ExportedEntry(mHashCollisionEntry));
+ }
+ }
+ return entries;
+ }
+
+ /** Removes all collected data. */
+ public void reset() {
+ synchronized (mLock) {
+ mEntries.clear();
+ }
+ synchronized (mHashCollisionEntry) {
+ mHashCollisionEntry.reset();
+ }
+ synchronized (mOverflowEntry) {
+ mOverflowEntry.reset();
+ }
+ }
+
+ public void setSamplingInterval(int samplingInterval) {
+ mSamplingInterval = samplingInterval;
+ }
+
+ @NonNull
+ private Entry getOrCreateEntry(Message msg) {
+ final boolean isInteractive = mDeviceState.isScreenInteractive();
+ final int id = Entry.idFor(msg, isInteractive);
+ Entry entry;
+ synchronized (mLock) {
+ entry = mEntries.get(id);
+ if (entry == null) {
+ if (mEntries.size() >= mEntriesSizeCap) {
+ // If over the size cap, track totals under a single entry.
+ return mOverflowEntry;
+ }
+ entry = new Entry(msg, isInteractive);
+ mEntries.put(id, entry);
+ }
+ }
+
+ if (entry.handler.getClass() != msg.getTarget().getClass()
+ || entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
+ || entry.isInteractive != isInteractive) {
+ // If a hash collision happened, track totals under a single entry.
+ return mHashCollisionEntry;
+ }
+ return entry;
+ }
+
+ private void recycleSession(DispatchSession session) {
+ if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+ mSessionPool.add(session);
+ }
+ }
+
+ protected long getThreadTimeMicro() {
+ return SystemClock.currentThreadTimeMicro();
+ }
+
+ protected long getElapsedRealtimeMicro() {
+ return SystemClock.elapsedRealtimeNanos() / 1000;
+ }
+
+ protected boolean shouldCollectDetailedData() {
+ return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ }
+
+ private static class DispatchSession {
+ static final DispatchSession NOT_SAMPLED = new DispatchSession();
+ public long startTimeMicro;
+ public long cpuStartMicro;
+ }
+
+ private static class Entry {
+ public final Handler handler;
+ public final String messageName;
+ public final boolean isInteractive;
+ public long messageCount;
+ public long recordedMessageCount;
+ public long exceptionCount;
+ public long totalLatencyMicro;
+ public long maxLatencyMicro;
+ public long cpuUsageMicro;
+ public long maxCpuUsageMicro;
+
+ Entry(Message msg, boolean isInteractive) {
+ this.handler = msg.getTarget();
+ this.messageName = handler.getMessageName(msg);
+ this.isInteractive = isInteractive;
+ }
+
+ Entry(String specialEntryName) {
+ this.messageName = specialEntryName;
+ this.handler = null;
+ this.isInteractive = false;
+ }
+
+ void reset() {
+ messageCount = 0;
+ recordedMessageCount = 0;
+ exceptionCount = 0;
+ totalLatencyMicro = 0;
+ maxLatencyMicro = 0;
+ cpuUsageMicro = 0;
+ maxCpuUsageMicro = 0;
+ }
+
+ static int idFor(Message msg, boolean isInteractive) {
+ int result = 7;
+ result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
+ result = 31 * result + msg.getTarget().getClass().hashCode();
+ result = 31 * result + (isInteractive ? 1231 : 1237);
+ if (msg.getCallback() != null) {
+ return 31 * result + msg.getCallback().getClass().hashCode();
+ } else {
+ return 31 * result + msg.what;
+ }
+ }
+ }
+
+ /** Aggregated data of Looper message dispatching in the in the current process. */
+ public static class ExportedEntry {
+ public final String handlerClassName;
+ public final String threadName;
+ public final String messageName;
+ public final boolean isInteractive;
+ public final long messageCount;
+ public final long recordedMessageCount;
+ public final long exceptionCount;
+ public final long totalLatencyMicros;
+ public final long maxLatencyMicros;
+ public final long cpuUsageMicros;
+ public final long maxCpuUsageMicros;
+
+ ExportedEntry(Entry entry) {
+ if (entry.handler != null) {
+ this.handlerClassName = entry.handler.getClass().getName();
+ this.threadName = entry.handler.getLooper().getThread().getName();
+ } else {
+ // Overflow/collision entries do not have a handler set.
+ this.handlerClassName = "";
+ this.threadName = "";
+ }
+ this.isInteractive = entry.isInteractive;
+ this.messageName = entry.messageName;
+ this.messageCount = entry.messageCount;
+ this.recordedMessageCount = entry.recordedMessageCount;
+ this.exceptionCount = entry.exceptionCount;
+ this.totalLatencyMicros = entry.totalLatencyMicro;
+ this.maxLatencyMicros = entry.maxLatencyMicro;
+ this.cpuUsageMicros = entry.cpuUsageMicro;
+ this.maxCpuUsageMicros = entry.maxCpuUsageMicro;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4b004e2..09ccf67 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -79,6 +79,8 @@
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
+ // This is kept due to @UnsupportedAppUsage.
+ // TODO(Bug 113914148): Consider removing this.
int getInputMethodWindowVisibleHeight();
void clearLastInputMethodWindowForTransition(in IBinder token);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f1bd63b..1257336 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -271,7 +271,7 @@
optional .android.graphics.RectProto containing_frame = 8 [deprecated=true];
optional .android.graphics.RectProto parent_frame = 9 [deprecated=true];
optional .android.graphics.RectProto content_frame = 10 [deprecated=true];
- optional .android.graphics.RectProto content_insets = 11;
+ optional .android.graphics.RectProto content_insets = 11 [deprecated=true];
optional .android.graphics.RectProto surface_insets = 12;
optional WindowStateAnimatorProto animator = 13;
optional bool animating_exit = 14;
@@ -288,10 +288,10 @@
optional .android.graphics.RectProto visible_frame = 26 [deprecated=true];
optional .android.graphics.RectProto decor_frame = 27 [deprecated=true];
optional .android.graphics.RectProto outset_frame = 28 [deprecated=true];
- optional .android.graphics.RectProto overscan_insets = 29;
- optional .android.graphics.RectProto visible_insets = 30;
- optional .android.graphics.RectProto stable_insets = 31;
- optional .android.graphics.RectProto outsets = 32;
+ optional .android.graphics.RectProto overscan_insets = 29 [deprecated=true];
+ optional .android.graphics.RectProto visible_insets = 30 [deprecated=true];
+ optional .android.graphics.RectProto stable_insets = 31 [deprecated=true];
+ optional .android.graphics.RectProto outsets = 32 [deprecated=true];
optional .android.view.DisplayCutoutProto cutout = 33 [deprecated=true];
optional bool remove_on_exit = 34;
optional bool destroying = 35;
@@ -380,4 +380,9 @@
optional .android.graphics.RectProto parent_frame = 8;
optional .android.graphics.RectProto visible_frame = 9;
optional .android.view.DisplayCutoutProto cutout = 10;
+ optional .android.graphics.RectProto content_insets = 11;
+ optional .android.graphics.RectProto overscan_insets = 12;
+ optional .android.graphics.RectProto visible_insets = 13;
+ optional .android.graphics.RectProto stable_insets = 14;
+ optional .android.graphics.RectProto outsets = 15;
}
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 3308128..13f6bce 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -40,7 +40,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=3</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index bb0d4d5..d45b91a 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -38,7 +38,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=3</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 1241a9d..b520d5d 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -41,7 +41,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
index 232f149..650aa62 100644
--- a/core/res/res/values-mcc302-mnc610/config.xml
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -28,7 +28,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
index 1d2e625..4bb68dc 100644
--- a/core/res/res/values-mcc302-mnc640/config.xml
+++ b/core/res/res/values-mcc302-mnc640/config.xml
@@ -24,7 +24,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index ef1ecd2..11bfa05 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -43,7 +43,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cf1320c..235f85b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2116,6 +2116,9 @@
<!-- Type of the reach sensor. Empty if reach is not supported. -->
<string name="config_dozeReachSensorType" translatable="false"></string>
+ <!-- Type of the wake up sensor. Empty if not supported. -->
+ <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
+
<!-- Control whether the always on display mode is available. This should only be enabled on
devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
states. -->
@@ -2793,7 +2796,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=0</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 48c263e..92cca72 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3462,4 +3462,5 @@
<java-symbol type="integer" name="db_wal_truncate_size" />
<java-symbol type="integer" name="config_wakeUpDelayDoze" />
+ <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
</resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 189a4aa..e84aed1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -192,6 +192,7 @@
Settings.Global.DEFAULT_DNS_SERVER,
Settings.Global.DEFAULT_INSTALL_LOCATION,
Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
+ Settings.Global.DEFAULT_USER_ID_TO_BOOT_INTO,
Settings.Global.DESK_DOCK_SOUND,
Settings.Global.DESK_UNDOCK_SOUND,
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
@@ -273,6 +274,7 @@
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+ Settings.Global.LAST_ACTIVE_USER_ID,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -280,6 +282,7 @@
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
+ Settings.Global.LOOPER_STATS,
Settings.Global.LOW_BATTERY_SOUND,
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
Settings.Global.LOW_POWER_MODE,
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index ace6b2d..364dcfd 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -18,10 +18,7 @@
import static org.junit.Assert.assertEquals;
-import android.content.Intent;
-import android.os.BatteryManager;
import android.os.Binder;
-import android.os.OsProtoEnums;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -34,7 +31,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -50,6 +46,7 @@
private static final int TEST_UID = 1;
private static final int REQUEST_SIZE = 2;
private static final int REPLY_SIZE = 3;
+ private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
@Test
public void testDetailedOff() {
@@ -388,43 +385,27 @@
}
@Test
- public void testDataResetWhenInitialStateSet() {
+ public void testNoDataCollectedBeforeInitialDeviceStateSet() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDeviceState(null);
bcs.setDetailedTracking(true);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
- bcs.setInitialState(true, true);
+ bcs.setDeviceState(mDeviceState.getReadonlyClient());
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
}
@Test
- public void testScreenAndChargerInitialStates() {
- TestBinderCallsStats bcs = new TestBinderCallsStats();
- bcs.setDetailedTracking(true);
- Binder binder = new Binder();
- bcs.setInitialState(true /** screen iteractive */, false);
-
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
- List<BinderCallsStats.CallStat> callStatsList =
- new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
- assertEquals(true, callStatsList.get(0).screenInteractive);
- }
-
- @Test
public void testNoDataCollectedOnCharger() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(true);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -436,7 +417,7 @@
public void testScreenOff() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+ mDeviceState.setScreenInteractive(false);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -453,7 +434,7 @@
public void testScreenOn() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+ mDeviceState.setScreenInteractive(true);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -470,9 +451,8 @@
public void testOnCharger() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(true);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -484,9 +464,8 @@
public void testOnBattery() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(false);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -522,7 +501,6 @@
public void testGetExportedStatsWhenDetailedTrackingEnabled() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
@@ -561,7 +539,7 @@
assertEquals(0, bcs.getExceptionCounts().size());
}
- static class TestBinderCallsStats extends BinderCallsStats {
+ class TestBinderCallsStats extends BinderCallsStats {
int callingUid = TEST_UID;
long time = 1234;
long elapsedTime = 0;
@@ -580,6 +558,7 @@
}
});
setSamplingInterval(1);
+ setDeviceState(mDeviceState.getReadonlyClient());
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
new file mode 100644
index 0000000..0eb3d06
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2018 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 com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Comparator;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public final class LooperStatsTest {
+ private HandlerThread mThreadFirst;
+ private HandlerThread mThreadSecond;
+ private Handler mHandlerFirst;
+ private Handler mHandlerSecond;
+ private Handler mHandlerAnonymous;
+ private CachedDeviceState mDeviceState;
+
+ @Before
+ public void setUp() {
+ // The tests are all single-threaded. HandlerThreads are created to allow creating Handlers
+ // and to test Thread name collection.
+ mThreadFirst = new HandlerThread("TestThread1");
+ mThreadSecond = new HandlerThread("TestThread2");
+ mThreadFirst.start();
+ mThreadSecond.start();
+
+ mHandlerFirst = new TestHandlerFirst(mThreadFirst.getLooper());
+ mHandlerSecond = new TestHandlerSecond(mThreadSecond.getLooper());
+ mHandlerAnonymous = new Handler(mThreadFirst.getLooper()) {
+ /* To create an anonymous subclass. */
+ };
+ mDeviceState = new CachedDeviceState();
+ mDeviceState.setCharging(false);
+ mDeviceState.setScreenInteractive(true);
+ }
+
+ @After
+ public void tearDown() {
+ mThreadFirst.quit();
+ mThreadSecond.quit();
+ }
+
+ @Test
+ public void testSingleMessageDispatched() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(1);
+ LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.threadName).isEqualTo("TestThread1");
+ assertThat(entry.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+ assertThat(entry.isInteractive).isEqualTo(true);
+ assertThat(entry.messageCount).isEqualTo(1);
+ assertThat(entry.recordedMessageCount).isEqualTo(1);
+ assertThat(entry.exceptionCount).isEqualTo(0);
+ assertThat(entry.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry.maxCpuUsageMicros).isEqualTo(10);
+ }
+
+ @Test
+ public void testThrewException() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
+ new ArithmeticException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(1);
+ LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.threadName).isEqualTo("TestThread1");
+ assertThat(entry.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry.messageName).isEqualTo("0x7" /* 7 in hex */);
+ assertThat(entry.isInteractive).isEqualTo(true);
+ assertThat(entry.messageCount).isEqualTo(0);
+ assertThat(entry.recordedMessageCount).isEqualTo(0);
+ assertThat(entry.exceptionCount).isEqualTo(1);
+ assertThat(entry.totalLatencyMicros).isEqualTo(0);
+ assertThat(entry.maxLatencyMicros).isEqualTo(0);
+ assertThat(entry.cpuUsageMicros).isEqualTo(0);
+ assertThat(entry.maxCpuUsageMicros).isEqualTo(0);
+ }
+
+ @Test
+ public void testMultipleMessagesDispatched() {
+ TestableLooperStats looperStats = new TestableLooperStats(2, 100);
+
+ // Contributes to entry2.
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ // Contributes to entry2.
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(50);
+ looperStats.tickThreadTime(20);
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1000));
+
+ // Contributes to entry3.
+ Object token3 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
+ }));
+
+ // Contributes to entry1.
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(100);
+ looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(3);
+ entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+ // Captures data for token4 call.
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.threadName).isEqualTo("TestThread1");
+ assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
+ assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
+ assertThat(entry1.messageCount).isEqualTo(1);
+ assertThat(entry1.recordedMessageCount).isEqualTo(0);
+ assertThat(entry1.exceptionCount).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(0);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(0);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(0);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+
+ // Captures data for token1 and token2 calls.
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.threadName).isEqualTo("TestThread1");
+ assertThat(entry2.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry2.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+ assertThat(entry2.messageCount).isEqualTo(2);
+ assertThat(entry2.recordedMessageCount).isEqualTo(1);
+ assertThat(entry2.exceptionCount).isEqualTo(0);
+ assertThat(entry2.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry2.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry2.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry2.maxCpuUsageMicros).isEqualTo(10);
+
+ // Captures data for token3 call.
+ LooperStats.ExportedEntry entry3 = entries.get(2);
+ assertThat(entry3.threadName).isEqualTo("TestThread2");
+ assertThat(entry3.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
+ assertThat(entry3.messageName).startsWith(
+ "com.android.internal.os.-$$Lambda$LooperStatsTest$");
+ assertThat(entry3.messageCount).isEqualTo(1);
+ assertThat(entry3.recordedMessageCount).isEqualTo(1);
+ assertThat(entry3.exceptionCount).isEqualTo(0);
+ assertThat(entry3.totalLatencyMicros).isEqualTo(10);
+ assertThat(entry3.maxLatencyMicros).isEqualTo(10);
+ assertThat(entry3.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry3.maxCpuUsageMicros).isEqualTo(10);
+ }
+
+ @Test
+ public void testDataNotCollectedBeforeDeviceStateSet() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ looperStats.setDeviceState(null);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
+ @Test
+ public void testDataNotCollectedOnCharger() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ mDeviceState.setCharging(true);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
+ @Test
+ public void testScreenStateCollected() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ mDeviceState.setScreenInteractive(true);
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ Object token3 = looperStats.messageDispatchStarting();
+ // If screen state changed during the call, we take the final state into account.
+ mDeviceState.setScreenInteractive(false);
+ looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1000));
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token4, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(2);
+ entries.sort(Comparator.comparing(e -> e.isInteractive));
+
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.isInteractive).isEqualTo(false);
+ assertThat(entry1.messageCount).isEqualTo(1);
+ assertThat(entry1.exceptionCount).isEqualTo(1);
+
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.isInteractive).isEqualTo(true);
+ assertThat(entry2.messageCount).isEqualTo(1);
+ assertThat(entry2.exceptionCount).isEqualTo(1);
+ }
+
+ @Test
+ public void testMessagesOverSizeCap() {
+ TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(50);
+ looperStats.tickThreadTime(20);
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1001));
+
+ Object token3 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1002));
+
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(1003));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(2);
+ entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.threadName).isEqualTo("");
+ assertThat(entry1.handlerClassName).isEqualTo("");
+ assertThat(entry1.messageName).isEqualTo("OVERFLOW");
+ assertThat(entry1.messageCount).isEqualTo(3);
+ assertThat(entry1.recordedMessageCount).isEqualTo(1);
+ assertThat(entry1.exceptionCount).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(10);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(10);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.threadName).isEqualTo("TestThread1");
+ assertThat(entry2.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ }
+
+ @Test
+ public void testInvalidTokensCauseException() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ assertThrows(ClassCastException.class,
+ () -> looperStats.dispatchingThrewException(new Object(),
+ mHandlerFirst.obtainMessage(),
+ new ArithmeticException()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(new Object(), mHandlerFirst.obtainMessage()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(123, mHandlerFirst.obtainMessage()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(mHandlerFirst.obtainMessage(),
+ mHandlerFirst.obtainMessage()));
+
+ assertThat(looperStats.getEntries()).hasSize(0);
+ }
+
+ @Test
+ public void testTracksMultipleHandlerInstancesIfSameClass() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Handler handlerFirstAnother = new TestHandlerFirst(mHandlerFirst.getLooper());
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token2, handlerFirstAnother.obtainMessage(1000));
+
+ assertThat(looperStats.getEntries()).hasSize(1);
+ assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
+ }
+
+ private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Expected " + exceptionClass + " to be thrown.");
+ } catch (Exception exception) {
+ assertThat(exception).isInstanceOf(exceptionClass);
+ }
+ }
+
+ private final class TestableLooperStats extends LooperStats {
+ private static final long INITIAL_MICROS = 10001000123L;
+ private int mCount;
+ private long mRealtimeMicros;
+ private long mThreadTimeMicros;
+ private int mSamplingInterval;
+
+ TestableLooperStats(int samplingInterval, int sizeCap) {
+ super(samplingInterval, sizeCap);
+ this.mSamplingInterval = samplingInterval;
+ this.setDeviceState(mDeviceState.getReadonlyClient());
+ }
+
+ void tickRealtime(long micros) {
+ mRealtimeMicros += micros;
+ }
+
+ void tickThreadTime(long micros) {
+ mThreadTimeMicros += micros;
+ }
+
+ @Override
+ protected long getElapsedRealtimeMicro() {
+ return INITIAL_MICROS + mRealtimeMicros;
+ }
+
+ @Override
+ protected long getThreadTimeMicro() {
+ return INITIAL_MICROS + mThreadTimeMicros;
+ }
+
+ @Override
+ protected boolean shouldCollectDetailedData() {
+ return mCount++ % mSamplingInterval == 0;
+ }
+ }
+
+ private static final class TestHandlerFirst extends Handler {
+ TestHandlerFirst(Looper looper) {
+ super(looper);
+ }
+ }
+
+ private static final class TestHandlerSecond extends Handler {
+ TestHandlerSecond(Looper looper) {
+ super(looper);
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9dab536..7fc1787 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.annotation.Px;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
@@ -805,25 +806,39 @@
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
*
* @return true if the underlineText bit is set in the paint's flags.
+ * @see #getUnderlinePosition()
+ * @see #getUnderlineThickness()
+ * @see #setUnderlineText(boolean)
*/
public final boolean isUnderlineText() {
return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
}
/**
- * Distance from top of the underline to the baseline. Positive values mean below the baseline.
- * This method returns where the underline should be drawn independent of if the underlineText
- * bit is set at the moment.
- * @hide
+ * Returns the distance from top of the underline to the baseline in pixels.
+ *
+ * The result is positive for positions that are below the baseline.
+ * This method returns where the underline should be drawn independent of if the {@link
+ * #UNDERLINE_TEXT_FLAG} bit is set.
+ *
+ * @return the position of the underline in pixels
+ * @see #isUnderlineText()
+ * @see #getUnderlineThickness()
+ * @see #setUnderlineText(boolean)
*/
- public float getUnderlinePosition() {
+ public @Px float getUnderlinePosition() {
return nGetUnderlinePosition(mNativePaint);
}
/**
- * @hide
+ * Returns the thickness of the underline in pixels.
+ *
+ * @return the thickness of the underline in pixels
+ * @see #isUnderlineText()
+ * @see #getUnderlinePosition()
+ * @see #setUnderlineText(boolean)
*/
- public float getUnderlineThickness() {
+ public @Px float getUnderlineThickness() {
return nGetUnderlineThickness(mNativePaint);
}
@@ -832,6 +847,9 @@
*
* @param underlineText true to set the underlineText bit in the paint's
* flags, false to clear it.
+ * @see #isUnderlineText()
+ * @see #getUnderlinePosition()
+ * @see #getUnderlineThickness()
*/
public void setUnderlineText(boolean underlineText) {
nSetUnderlineText(mNativePaint, underlineText);
@@ -840,26 +858,40 @@
/**
* Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
*
- * @return true if the strikeThruText bit is set in the paint's flags.
+ * @return true if the {@link #STRIKE_THRU_TEXT_FLAG} bit is set in the paint's flags.
+ * @see #getStrikeThruPosition()
+ * @see #getStrikeThruThickness()
+ * @see #setStrikeThruText(boolean)
*/
public final boolean isStrikeThruText() {
return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
}
/**
- * Distance from top of the strike-through line to the baseline. Negative values mean above the
- * baseline. This method returns where the strike-through line should be drawn independent of if
- * the strikeThruText bit is set at the moment.
- * @hide
+ * Distance from top of the strike-through line to the baseline in pixels.
+ *
+ * The result is negative for positions that are above the baseline.
+ * This method returns where the strike-through line should be drawn independent of if the
+ * {@link #STRIKE_THRU_TEXT_FLAG} bit is set.
+ *
+ * @return the position of the strike-through line in pixels
+ * @see #getStrikeThruThickness()
+ * @see #setStrikeThruText(boolean)
+ * @see #isStrikeThruText()
*/
- public float getStrikeThruPosition() {
+ public @Px float getStrikeThruPosition() {
return nGetStrikeThruPosition(mNativePaint);
}
/**
- * @hide
+ * Returns the thickness of the strike-through line in pixels.
+ *
+ * @return the position of the strike-through line in pixels
+ * @see #getStrikeThruPosition()
+ * @see #setStrikeThruText(boolean)
+ * @see #isStrikeThruText()
*/
- public float getStrikeThruThickness() {
+ public @Px float getStrikeThruThickness() {
return nGetStrikeThruThickness(mNativePaint);
}
@@ -868,6 +900,9 @@
*
* @param strikeThruText true to set the strikeThruText bit in the paint's
* flags, false to clear it.
+ * @see #getStrikeThruPosition()
+ * @see #getStrikeThruThickness()
+ * @see #isStrikeThruText()
*/
public void setStrikeThruText(boolean strikeThruText) {
nSetStrikeThruText(mNativePaint, strikeThruText);
@@ -1560,22 +1595,25 @@
}
/**
- * Return the paint's word-spacing for text. The default value is 0.
+ * Return the paint's extra word-spacing for text.
*
- * @return the paint's word-spacing for drawing text.
- * @hide
+ * The default value is 0.
+ *
+ * @return the paint's extra word-spacing for drawing text in pixels.
+ * @see #setWordSpacing(float)
*/
public float getWordSpacing() {
return nGetWordSpacing(mNativePaint);
}
/**
- * Set the paint's word-spacing for text. The default value is 0.
- * The value is in pixels (note the units are not the same as for
- * letter-spacing).
+ * Set the paint's extra word-spacing for text.
*
- * @param wordSpacing set the paint's word-spacing for drawing text.
- * @hide
+ * Increases the white space width between words with the given amount of pixels.
+ * The default value is 0.
+ *
+ * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
+ * @see #getWordSpacing()
*/
public void setWordSpacing(float wordSpacing) {
nSetWordSpacing(mNativePaint, wordSpacing);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 8de8f81..2855227 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -280,7 +280,7 @@
* @see #getStateDrawable(int)
* @see #getStateSet(int)
*/
- public int getStateDrawableIndex(int[] stateSet) {
+ public int findStateDrawableIndex(int[] stateSet) {
return mStateListState.indexOfStateSet(stateSet);
}
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index d985633..e8332a8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -178,8 +178,8 @@
struct stat st = {};
LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size));
- sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release());
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size));
+ sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index e8bc622..0476222 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1035,7 +1035,7 @@
return fromGetVolumeControlStream ?
AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
}
- if ((aa.getFlags() & FLAG_SCO) == FLAG_SCO) {
+ if ((aa.getAllFlags() & FLAG_SCO) == FLAG_SCO) {
return fromGetVolumeControlStream ?
AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
}
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 0892f73..967e13f 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:gravity="center_vertical"
- android:visibility="invisible">
+ android:alpha="0">
<ImageView
android:id="@+id/next_alarm_icon"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d051f07..26eadb5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -361,7 +361,7 @@
<dimen name="qs_quick_layout_width">-1px</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
- <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
+ <dimen name="qs_header_tile_margin_horizontal">4dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index 0d25c91..0cde9da 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -146,8 +147,9 @@
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
- mWindowManager.destroyInputConsumer(mName);
- mWindowManager.createInputConsumer(mToken, mName, inputChannel);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
@@ -164,7 +166,8 @@
public void unregisterInputConsumer() {
if (mInputEventReceiver != null) {
try {
- mWindowManager.destroyInputConsumer(mName);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
} catch (RemoteException e) {
Log.e(TAG, "Failed to destroy input consumer", e);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 5605b7a..ac8f024 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -39,6 +39,7 @@
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.systemui.util.EmergencyDialerConstants;
/**
* This class implements a smart emergency button that updates itself based
@@ -48,11 +49,13 @@
*/
public class EmergencyButton extends Button {
private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
- .setAction("com.android.phone.EmergencyDialer.DIAL")
+ .setAction(EmergencyDialerConstants.ACTION_DIAL)
.setPackage("com.android.phone")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
private static final String LOG_TAG = "EmergencyButton";
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 51cc4a1..ab30f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final int PULSE_REASONS = 7;
+ private static final int REASONS = 8;
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -45,6 +45,7 @@
public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
public static final int PULSE_REASON_SENSOR_REACH = 6;
+ public static final int REASON_SENSOR_WAKE_UP = 7;
private static boolean sRegisterKeyguardCallback = true;
@@ -74,7 +75,7 @@
public static void tracePulseStart(int reason) {
if (!ENABLED) return;
sPulsing = true;
- log("pulseStart reason=" + pulseReasonToString(reason));
+ log("pulseStart reason=" + reasonToString(reason));
}
public static void tracePulseFinish() {
@@ -102,8 +103,8 @@
sScreenOnPulsingStats = new SummaryStats();
sScreenOnNotPulsingStats = new SummaryStats();
sEmergencyCallStats = new SummaryStats();
- sProxStats = new SummaryStats[PULSE_REASONS][2];
- for (int i = 0; i < PULSE_REASONS; i++) {
+ sProxStats = new SummaryStats[REASONS][2];
+ for (int i = 0; i < REASONS; i++) {
sProxStats[i][0] = new SummaryStats();
sProxStats[i][1] = new SummaryStats();
}
@@ -176,15 +177,15 @@
}
public static void traceProximityResult(Context context, boolean near, long millis,
- int pulseReason) {
+ int reason) {
if (!ENABLED) return;
init(context);
- log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
+ log("proximityResult reason=" + reasonToString(reason) + " near=" + near
+ " millis=" + millis);
- sProxStats[pulseReason][near ? 0 : 1].append();
+ sProxStats[reason][near ? 0 : 1].append();
}
- public static String pulseReasonToString(int pulseReason) {
+ public static String reasonToString(int pulseReason) {
switch (pulseReason) {
case PULSE_REASON_INTENT: return "intent";
case PULSE_REASON_NOTIFICATION: return "notification";
@@ -193,6 +194,7 @@
case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
case PULSE_REASON_SENSOR_REACH: return "reach";
+ case REASON_SENSOR_WAKE_UP: return "wakeup";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
}
@@ -218,8 +220,8 @@
sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
sEmergencyCallStats.dump(pw, "Emergency call");
- for (int i = 0; i < PULSE_REASONS; i++) {
- final String reason = pulseReasonToString(i);
+ for (int i = 0; i < REASONS; i++) {
+ final String reason = reasonToString(i);
sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
}
@@ -262,10 +264,10 @@
}
}
- public static void traceSensor(Context context, int pulseReason) {
+ public static void traceSensor(Context context, int reason) {
if (!ENABLED) return;
init(context);
- log("sensor type=" + pulseReasonToString(pulseReason));
+ log("sensor type=" + reasonToString(reason));
}
private static class SummaryStats {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 045a98c..f9dfb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -63,6 +63,7 @@
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
+ private final Consumer<Boolean> mWakeScreenCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
@@ -70,9 +71,9 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
- DozeParameters dozeParameters,
- AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
- Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
+ DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
+ Callback callback, Consumer<Boolean> proxCallback,
+ Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -80,6 +81,7 @@
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
+ mWakeScreenCallback = wakeScreenCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -117,6 +119,7 @@
DozeLog.PULSE_REASON_SENSOR_REACH,
false /* reports touch coordinates */,
false /* touchscreen */),
+ new WakeScreenSensor(),
};
mProxSensor = new ProxSensor(policy);
@@ -302,9 +305,9 @@
final boolean mSettingDefault;
final boolean mRequiresTouchscreen;
- private boolean mRequested;
- private boolean mRegistered;
- private boolean mDisabled;
+ protected boolean mRequested;
+ protected boolean mRegistered;
+ protected boolean mDisabled;
public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
@@ -348,7 +351,7 @@
}
}
- private boolean enabledBySetting() {
+ protected boolean enabledBySetting() {
if (TextUtils.isEmpty(mSetting)) {
return true;
}
@@ -401,7 +404,7 @@
}
}
- private String triggerEventToString(TriggerEvent event) {
+ protected String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
.append(event.timestamp).append(',')
@@ -415,6 +418,28 @@
}
}
+ private class WakeScreenSensor extends TriggerSensor {
+
+ WakeScreenSensor() {
+ super(findSensorWithType(mConfig.wakeScreenSensorType()),
+ Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
+ DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
+ false /* requiresTouchscreen */);
+ }
+
+ @Override
+ @AnyThread
+ public void onTrigger(TriggerEvent event) {
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ mRegistered = false;
+ mWakeScreenCallback.accept(event.values[0] > 0);
+ updateListener(); // reregister, this sensor only fires once
+ }));
+ }
+ }
+
public interface Callback {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 73cbd7d..1589969 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar,
+ config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -103,7 +103,7 @@
private void proximityCheckThenCall(IntConsumer callback,
boolean alreadyPerformedProxCheck,
- int pulseReason) {
+ int reason) {
Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
if (alreadyPerformedProxCheck) {
callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
@@ -116,7 +116,7 @@
public void onProximityResult(int result) {
final long end = SystemClock.uptimeMillis();
DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, pulseReason);
+ end - start, reason);
callback.accept(result);
}
}.check();
@@ -182,6 +182,28 @@
}
}
+ private void onWakeScreen(boolean wake) {
+ DozeMachine.State state = mMachine.getState();
+ boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
+ boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+
+ if (wake) {
+ proximityCheckThenCall((result) -> {
+ if (result == ProximityCheck.RESULT_NEAR) {
+ // In pocket, drop event.
+ return;
+ }
+ if (pausing || paused) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
+ }
+ }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+ } else {
+ if (!pausing && !paused) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+ }
+ }
+ }
+
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 06d4434..cc1b9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -89,6 +89,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
import java.util.ArrayList;
@@ -449,9 +450,6 @@
}
private class EmergencyDialerAction extends SinglePressAction {
- private static final String ACTION_EMERGENCY_DIALER_DIAL =
- "com.android.phone.EmergencyDialer.DIAL";
-
private EmergencyDialerAction() {
super(R.drawable.ic_faster_emergency,
R.string.global_action_emergency);
@@ -460,8 +458,10 @@
@Override
public void onPress() {
MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
- Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
+ Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ff5ac76..b988c55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -547,38 +547,23 @@
/**
* Fades in the updated status text. Note that if there's already a status showing, this will
- * immediately hide it and fade in the updated status.
+ * immediately fade it out and fade in the updated status.
*/
private void showStatus() {
mStatusContainer.setAlpha(0f);
- mStatusContainer.setVisibility(View.VISIBLE);
- // Animate the alarm back in. Make sure to clear the animator listener for the animation!
mStatusContainer.animate()
.alpha(1f)
.setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(null)
.start();
}
- /** Fades out and hides the status text. */
+ /** Fades out the status text. */
private void hideStatusText() {
- if (mStatusContainer.getVisibility() == View.VISIBLE) {
- mStatusContainer.animate()
- .alpha(0f)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Log.d(TAG, "hideAlarmText: Hid alarm text");
-
- // Reset the alpha regardless of how the animation ends for the next
- // time we show this view/want to animate it.
- mStatusContainer.setVisibility(View.INVISIBLE);
- mStatusContainer.setAlpha(1f);
- }
- })
- .start();
- }
+ mStatusContainer.animate()
+ .alpha(0f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .start();
}
public void updateEverything() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index afd64f3..0d3ba77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -45,7 +45,7 @@
public void onDisplayBlanked() {
if (DEBUG) {
Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
- + DozeLog.pulseReasonToString(mPulseReason));
+ + DozeLog.reasonToString(mPulseReason));
}
if (!mDozing) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 238407a..a46f018 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,17 +16,13 @@
package com.android.systemui.usb;
-import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -45,13 +41,8 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.internal.util.XmlUtils;
-import android.hardware.usb.AccessoryFilter;
-import android.hardware.usb.DeviceFilter;
import com.android.systemui.R;
-import org.xmlpull.v1.XmlPullParser;
-
public class UsbPermissionActivity extends AlertActivity
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
@@ -71,12 +62,13 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Intent intent = getIntent();
+ Intent intent = getIntent();
mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- mPackageName = intent.getStringExtra("package");
+ mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
+ boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
PackageManager packageManager = getPackageManager();
ApplicationInfo aInfo;
@@ -105,121 +97,27 @@
ap.mPositiveButtonListener = this;
ap.mNegativeButtonListener = this;
- try {
- PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
- PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-
- if ((mDevice != null && canBeDefault(mDevice, packageInfo))
- || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
- // add "open when" checkbox
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
- mAccessory.getDescription()));
- } else {
- mAlwaysUse.setText(getString(R.string.always_use_device, appName,
- mDevice.getProductName()));
- }
- mAlwaysUse.setOnCheckedChangeListener(this);
-
- mClearDefaultHint = (TextView)ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
+ if (canBeDefault && (mDevice != null || mAccessory != null)) {
+ // add "open when" checkbox
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ if (mDevice == null) {
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
+ } else {
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
}
- } catch (PackageManager.NameNotFoundException e) {
- // ignore
+ mAlwaysUse.setOnCheckedChangeListener(this);
+
+ mClearDefaultHint = (TextView)ap.mView.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
}
setupAlert();
-
- }
-
- /**
- * Can the app be the default for the USB device. I.e. can the app be launched by default if
- * the device is plugged in.
- *
- * @param device The device the app would be default for
- * @param packageInfo The package info of the app
- *
- * @return {@code true} iff the app can be default
- */
- private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
- ActivityInfo[] activities = packageInfo.activities;
- if (activities != null) {
- int numActivities = activities.length;
- for (int i = 0; i < numActivities; i++) {
- ActivityInfo activityInfo = activities[i];
-
- try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
- UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
- if (parser == null) {
- continue;
- }
-
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- if ("usb-device".equals(parser.getName())) {
- DeviceFilter filter = DeviceFilter.read(parser);
- if (filter.matches(device)) {
- return true;
- }
- }
-
- XmlUtils.nextElement(parser);
- }
- } catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
- }
- }
- }
-
- return false;
- }
-
- /**
- * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
- * the accessory is plugged in.
- *
- * @param accessory The accessory the app would be default for
- * @param packageInfo The package info of the app
- *
- * @return {@code true} iff the app can be default
- */
- private boolean canBeDefault(@NonNull UsbAccessory accessory,
- @NonNull PackageInfo packageInfo) {
- ActivityInfo[] activities = packageInfo.activities;
- if (activities != null) {
- int numActivities = activities.length;
- for (int i = 0; i < numActivities; i++) {
- ActivityInfo activityInfo = activities[i];
-
- try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
- UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
- if (parser == null) {
- continue;
- }
-
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- if ("usb-accessory".equals(parser.getName())) {
- AccessoryFilter filter = AccessoryFilter.read(parser);
- if (filter.matches(accessory)) {
- return true;
- }
- }
-
- XmlUtils.nextElement(parser);
- }
- } catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
- }
- }
- }
-
- return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
new file mode 100644
index 0000000..d101ccb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+/**
+ * Constants defined and used in emergency dialer.
+ * Please keep these constants being consistent with those in com.android.phone.EmergencyDialer.
+ */
+public class EmergencyDialerConstants {
+ // Intent action for emergency dialer activity.
+ public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+ /**
+ * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+ * the emergency dialer.
+ */
+ public static final String EXTRA_ENTRY_TYPE =
+ "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
+ // Indicating the entrance to emergency dialer
+ public static final int ENTRY_TYPE_UNKNOWN = 0;
+ public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+ public static final int ENTRY_TYPE_POWER_MENU = 2;
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b668623..7edcdcb 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6508,6 +6508,10 @@
// OS: Q
ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569;
+ // OPEN: Settings > System > Input & Gesture > Wake screen
+ // OS: Q
+ SETTINGS_GESTURE_WAKE_SCREEN = 1570;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 50f15ca0..a85b69b 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -399,6 +399,12 @@
private void update(android.hardware.health.V2_0.HealthInfo info) {
traceBegin("HealthInfoUpdate");
+
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
+ info.legacy.batteryChargeCounter);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
+ info.legacy.batteryCurrent);
+
synchronized (mLock) {
if (!mUpdatesStopped) {
mHealthInfo = info.legacy;
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 9a7c345..15673a7 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -33,7 +33,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.BinderInternal;
+import com.android.internal.os.CachedDeviceState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -41,7 +41,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
public class BinderCallsStatsService extends Binder {
@@ -156,8 +155,10 @@
@Override
public void onBootPhase(int phase) {
if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ CachedDeviceState.Readonly deviceState = getLocalService(
+ CachedDeviceState.Readonly.class);
mService.systemReady(getContext());
- mBinderCallsStats.systemReady(getContext());
+ mBinderCallsStats.setDeviceState(deviceState);
}
}
}
diff --git a/services/core/java/com/android/server/CachedDeviceStateService.java b/services/core/java/com/android/server/CachedDeviceStateService.java
new file mode 100644
index 0000000..38269d3
--- /dev/null
+++ b/services/core/java/com/android/server/CachedDeviceStateService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.util.Slog;
+
+import com.android.internal.os.CachedDeviceState;
+
+/**
+ * Tracks changes to the device state (e.g. charging/on battery, screen on/off) to share it with
+ * the System Server telemetry services.
+ *
+ * @hide Only for use within the system server.
+ */
+public class CachedDeviceStateService extends SystemService {
+ private static final String TAG = "CachedDeviceStateService";
+ private final CachedDeviceState mDeviceState = new CachedDeviceState();
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_CHANGED:
+ mDeviceState.setCharging(
+ intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,
+ OsProtoEnums.BATTERY_PLUGGED_NONE)
+ != OsProtoEnums.BATTERY_PLUGGED_NONE);
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ mDeviceState.setScreenInteractive(true);
+ break;
+ case Intent.ACTION_SCREEN_OFF:
+ mDeviceState.setScreenInteractive(false);
+ break;
+ }
+ }
+ };
+
+ public CachedDeviceStateService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(CachedDeviceState.Readonly.class, mDeviceState.getReadonlyClient());
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ getContext().registerReceiver(mBroadcastReceiver, filter);
+ mDeviceState.setCharging(queryIsCharging());
+ mDeviceState.setScreenInteractive(queryScreenInteractive(getContext()));
+ }
+ }
+
+ private boolean queryIsCharging() {
+ final BatteryManagerInternal batteryManager =
+ LocalServices.getService(BatteryManagerInternal.class);
+ if (batteryManager == null) {
+ Slog.wtf(TAG, "BatteryManager null while starting CachedDeviceStateService");
+ // Default to true to not collect any data.
+ return true;
+ } else {
+ return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+ }
+ }
+
+ private boolean queryScreenInteractive(Context context) {
+ final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ Slog.wtf(TAG, "PowerManager null while starting CachedDeviceStateService");
+ return false;
+ } else {
+ return powerManager.isInteractive();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4d3468e..617e803 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3145,6 +3145,14 @@
return;
}
+ /**
+ * This is kept due to {@link android.annotation.UnsupportedAppUsage} in
+ * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
+ * {@link InputMethodService#onCreate()}.
+ *
+ * <p>TODO(Bug 113914148): Check if we can remove this.</p>
+ * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()}
+ */
@Override
public int getInputMethodWindowVisibleHeight() {
return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
@@ -4386,12 +4394,9 @@
private static final class LocalServiceImpl extends InputMethodManagerInternal {
@NonNull
private final InputMethodManagerService mService;
- @NonNull
- private final Handler mHandler;
LocalServiceImpl(@NonNull InputMethodManagerService service) {
mService = service;
- mHandler = service.mHandler;
}
@Override
@@ -4411,19 +4416,19 @@
@Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
- interactive ? 1 : 0, 0));
+ mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
public void hideCurrentInputMethod() {
- mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
- mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
}
@Override
public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName));
+ mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
}
}
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
new file mode 100644
index 0000000..23b30cc
--- /dev/null
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public class LooperStatsService extends Binder {
+ private static final String TAG = "LooperStatsService";
+ private static final String LOOPER_STATS_SERVICE_NAME = "looper_stats";
+ private static final String SETTINGS_ENABLED_KEY = "enabled";
+ private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String DEBUG_SYS_LOOPER_STATS_ENABLED =
+ "debug.sys.looper_stats_enabled";
+ private static final int DEFAULT_SAMPLING_INTERVAL = 100;
+ private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000;
+ private static final boolean DEFAULT_ENABLED = false;
+
+ private final Context mContext;
+ private final LooperStats mStats;
+ private boolean mEnabled = false;
+
+ private LooperStatsService(Context context, LooperStats stats) {
+ this.mContext = context;
+ this.mStats = stats;
+ }
+
+ private void initFromSettings() {
+ final KeyValueListParser parser = new KeyValueListParser(',');
+
+ try {
+ parser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.LOOPER_STATS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad looper_stats settings", e);
+ }
+
+ setSamplingInterval(
+ parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, DEFAULT_SAMPLING_INTERVAL));
+ // Manually specified value takes precedence over Settings.
+ setEnabled(SystemProperties.getBoolean(
+ DEBUG_SYS_LOOPER_STATS_ENABLED,
+ parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED)));
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new LooperShellCommand()).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ List<LooperStats.ExportedEntry> entries = mStats.getEntries();
+ entries.sort(Comparator
+ .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+ .thenComparing(entry -> entry.handlerClassName)
+ .thenComparing(entry -> entry.messageName));
+ String header = String.join(",", Arrays.asList(
+ "thread_name",
+ "handler_class",
+ "message_name",
+ "is_interactive",
+ "message_count",
+ "recorded_message_count",
+ "total_latency_micros",
+ "max_latency_micros",
+ "total_cpu_micros",
+ "max_cpu_micros",
+ "exception_count"));
+ pw.println(header);
+ for (LooperStats.ExportedEntry entry : entries) {
+ pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
+ entry.handlerClassName, entry.messageName, entry.isInteractive,
+ entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
+ entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
+ entry.exceptionCount);
+ }
+ }
+
+ private void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ mStats.reset();
+ Looper.setObserver(enabled ? mStats : null);
+ }
+ }
+
+ private void setSamplingInterval(int samplingInterval) {
+ mStats.setSamplingInterval(samplingInterval);
+ }
+
+ /**
+ * Manages the lifecycle of LooperStatsService within System Server.
+ */
+ public static class Lifecycle extends SystemService {
+ private final SettingsObserver mSettingsObserver;
+ private final LooperStatsService mService;
+ private final LooperStats mStats;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mStats = new LooperStats(DEFAULT_SAMPLING_INTERVAL, DEFAULT_ENTRIES_SIZE_CAP);
+ mService = new LooperStatsService(getContext(), mStats);
+ mSettingsObserver = new SettingsObserver(mService);
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(LooperStats.class, mStats);
+ // TODO: publish LooperStatsService as a binder service when the SE Policy is changed.
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ mService.initFromSettings();
+ Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LOOPER_STATS);
+ getContext().getContentResolver().registerContentObserver(
+ settingsUri, false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ mStats.setDeviceState(getLocalService(CachedDeviceState.Readonly.class));
+ }
+ }
+ }
+
+ private static class SettingsObserver extends ContentObserver {
+ private final LooperStatsService mService;
+
+ SettingsObserver(LooperStatsService service) {
+ super(BackgroundThread.getHandler());
+ mService = service;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ mService.initFromSettings();
+ }
+ }
+
+ private class LooperShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ if ("enable".equals(cmd)) {
+ setEnabled(true);
+ return 0;
+ } else if ("disable".equals(cmd)) {
+ setEnabled(false);
+ return 0;
+ } else if ("reset".equals(cmd)) {
+ mStats.reset();
+ return 0;
+ } else {
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
+ pw.println(" enable: Enable collecting stats");
+ pw.println(" disable: Disable collecting stats");
+ pw.println(" reset: Reset stats");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b91a449..e6ff0d8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17061,7 +17061,7 @@
activeInstr.mUiAutomationConnection = uiAutomationConnection;
activeInstr.mResultClass = className;
- boolean disableHiddenApiChecks = ai.usesNonSdkApi
+ boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
if (disableHiddenApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9df9ba6..a050aa9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,26 +16,10 @@
package com.android.server.input;
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+
import android.annotation.NonNull;
-import android.os.LocaleList;
-import android.os.ShellCallback;
-import android.util.Log;
-import android.view.Display;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.R;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-import com.android.server.DisplayThread;
-import com.android.server.LocalServices;
-import com.android.server.Watchdog;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.Manifest;
import android.app.IInputForwarder;
import android.app.Notification;
import android.app.NotificationManager;
@@ -50,8 +34,8 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
@@ -61,10 +45,10 @@
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
+import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
-import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.os.Binder;
@@ -72,6 +56,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
@@ -81,6 +66,7 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -97,6 +83,23 @@
import android.view.ViewConfiguration;
import android.widget.Toast;
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
+import com.android.server.Watchdog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import org.xmlpull.v1.XmlPullParser;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -113,9 +116,6 @@
import java.util.Locale;
import java.util.Objects;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
@@ -212,7 +212,8 @@
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
int policyFlags);
private static native void nativeToggleCapsLock(long ptr, int deviceId);
- private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
+ private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles,
+ int displayId);
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
private static native void nativeSetFocusedApplication(long ptr,
@@ -292,11 +293,6 @@
/** Switch code: Camera lens cover. When set the lens is covered. */
public static final int SW_CAMERA_LENS_COVER = 0x09;
- // Viewport constants defined in InputReader.h.
- public static final int VIEWPORT_DEFAULT = 1;
- public static final int VIEWPORT_EXTERNAL = 2;
- public static final int VIEWPORT_VIRTUAL = 3;
-
public static final int SW_LID_BIT = 1 << SW_LID;
public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -362,7 +358,7 @@
updateAccessibilityLargePointerFromSettings();
}
- // TODO(BT) Pass in paramter for bluetooth system
+ // TODO(BT) Pass in parameter for bluetooth system
public void systemRunning() {
if (DEBUG) {
Slog.d(TAG, "System ready.");
@@ -417,7 +413,7 @@
DisplayViewport externalTouchViewport,
List<DisplayViewport> virtualTouchViewports) {
if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_DEFAULT, defaultViewport);
+ setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
}
if (externalTouchViewport.valid) {
@@ -1467,7 +1463,7 @@
}
public void setInputWindows(InputWindowHandle[] windowHandles,
- InputWindowHandle focusedWindowHandle) {
+ InputWindowHandle focusedWindowHandle, int displayId) {
final IWindow newFocusedWindow =
focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
if (mFocusedWindow != newFocusedWindow) {
@@ -1476,7 +1472,7 @@
setPointerCapture(false);
}
}
- nativeSetInputWindows(mPtr, windowHandles);
+ nativeSetInputWindows(mPtr, windowHandles, displayId);
}
public void setFocusedApplication(InputApplicationHandle application) {
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 720eaaa5..bb29bf8 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -17,8 +17,8 @@
package com.android.server.input;
import android.graphics.Region;
-import android.view.InputChannel;
import android.view.IWindow;
+import android.view.InputChannel;
/**
* Functions as a handle for a window that can receive input.
@@ -106,7 +106,7 @@
@Override
public String toString() {
- return new StringBuilder(name)
+ return new StringBuilder(name != null ? name : "")
.append(", layer=").append(layer)
.append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
.append(frameRight).append(",").append(frameBottom).append("]")
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index a54811b..31cf9e3 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -69,7 +69,6 @@
import android.provider.Settings;
import android.provider.Telephony.Carriers;
import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
@@ -1577,6 +1576,8 @@
mSingleShot = false;
native_stop();
mLastFixTime = 0;
+ // native_stop() may reset the position mode in hardware.
+ mLastPositionMode = null;
// reset SV count to zero
updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 41c0be6..b922e40a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -70,6 +70,7 @@
import com.android.internal.os.KernelUidCpuTimeReader;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.util.DumpUtils;
import com.android.server.BinderCallsStatsService;
@@ -938,6 +939,29 @@
}
}
+ private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ LooperStats looperStats = LocalServices.getService(LooperStats.class);
+ if (looperStats == null) {
+ return;
+ }
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ for (LooperStats.ExportedEntry entry : entries) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */);
+ e.writeLong(0); // uid collection not implemented yet
+ e.writeString(entry.handlerClassName);
+ e.writeString(entry.threadName);
+ e.writeString(entry.messageName);
+ e.writeLong(entry.messageCount);
+ e.writeLong(entry.exceptionCount);
+ e.writeLong(entry.recordedMessageCount);
+ e.writeLong(entry.totalLatencyMicros);
+ e.writeLong(entry.cpuUsageMicros);
+ pulledData.add(e);
+ }
+ }
+
/**
* Pulls various data.
*/
@@ -1028,6 +1052,10 @@
pullBinderCallsStatsExceptions(tagId, ret);
break;
}
+ case StatsLog.LOOPER_STATS: {
+ pullLooperStats(tagId, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b775918..6da9f10 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -32,29 +32,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
+import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
@@ -78,6 +60,23 @@
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -1845,8 +1844,8 @@
surfaceInsets = win.getAttrs().surfaceInsets;
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the frame we chose in the if-blocks above.
- insets.set(win.mContentInsets);
- stableInsets.set(win.mStableInsets);
+ win.getContentInsets(insets);
+ win.getStableInsets(stableInsets);
}
if (mLaunchTaskBehind) {
@@ -2099,7 +2098,7 @@
WindowState win = findMainWindow();
Rect appRect = win != null ? win.getContentFrameLw() :
new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
- Rect insets = win != null ? win.mContentInsets : null;
+ final Rect insets = win != null ? win.getContentInsets() : null;
final Configuration displayConfig = mDisplayContent.getConfiguration();
return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(
appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 32fa9bf..d3e534c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -814,9 +814,7 @@
// {@link DisplayContent} ready for use.
mDisplayReady = true;
- // TODO(b/112081256): Use independent InputMonitor.
- mInputMonitor = isDefaultDisplay ? new InputMonitor(service, mDisplayId)
- : mService.getDefaultDisplayContentLocked().mInputMonitor;
+ mInputMonitor = new InputMonitor(service, mDisplayId);
}
boolean isReady() {
@@ -1426,7 +1424,7 @@
config.densityDpi = displayInfo.logicalDensityDpi;
config.colorMode =
- (displayInfo.isHdr()
+ ((displayInfo.isHdr() && mService.hasHdrSupport())
? Configuration.COLOR_MODE_HDR_YES
: Configuration.COLOR_MODE_HDR_NO)
| (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
@@ -2185,6 +2183,7 @@
mRemovingDisplay = false;
}
+ mInputMonitor.onRemoved();
mService.onDisplayRemoved(mDisplayId);
}
@@ -3009,7 +3008,7 @@
if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
w.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
- w.mLastContentInsets.set(-1, -1, -1, -1);
+ w.resetLastContentInsets();
mService.mWaitingForDrawn.add(w);
}
}, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 6a08f4d..585a4f5 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -20,9 +20,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.view.Display;
import android.view.InputChannel;
import android.view.WindowManager;
+
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c4beb55..3309798 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -367,6 +367,13 @@
}
}
+ void onRemoved() {
+ // If DisplayContent removed, we need find a way to remove window handles of this display
+ // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+ mDisplayId);
+ }
+
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
InputConsumerImpl navInputConsumer;
InputConsumerImpl pipInputConsumer;
@@ -399,8 +406,7 @@
this.inDrag = inDrag;
wallpaperController = mService.mRoot.mWallpaperController;
- // TODO(b/112081256): Use independent InputMonitor for each display.
- mService.mRoot/*.getDisplayContent(mDisplayId)*/.forAllWindows(this,
+ mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
true /* traverseTopToBottom */);
if (mAddWallpaperInputConsumerHandle) {
// No visible wallpaper found, add the wallpaper input consumer at the end.
@@ -408,8 +414,8 @@
}
// Send windows to native code.
- // TODO: Update Input windows and focus by display?
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+ mDisplayId);
clearInputWindowHandlesLw();
@@ -429,7 +435,8 @@
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
final int type = w.mAttrs.type;
- final boolean hasFocus = w == mInputFocus;
+ // TODO(b/111361570): multi-display focus, one focus window per display.
+ final boolean hasFocus = w.isFocused();
final boolean isVisible = w.isVisibleLw();
if (mAddRecentsAnimationInputConsumerHandle) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4dbd858..1eae567 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -373,7 +373,7 @@
: null;
final Rect contentInsets;
if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) {
- contentInsets = mTargetAppToken.findMainWindow().mContentInsets;
+ contentInsets = mTargetAppToken.findMainWindow().getContentInsets();
} else {
// If the window for the activity had not yet been created, use the display insets.
mService.getStableInsets(mDisplayId, mTmpRect);
@@ -583,7 +583,8 @@
if (mainWindow == null) {
return null;
}
- final Rect insets = new Rect(mainWindow.mContentInsets);
+ final Rect insets = new Rect();
+ mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
!topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 67ef471..00422e3 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -305,7 +305,8 @@
|| mCapturedLeash == null) {
return null;
}
- final Rect insets = new Rect(mainWindow.mContentInsets);
+ final Rect insets = new Rect();
+ mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6c8572a..b7507a4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -303,7 +303,7 @@
private Rect getInsets(WindowState state) {
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the task bounds.
- final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+ final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
return insets;
}
@@ -373,7 +373,7 @@
node.setClipToBounds(false);
final DisplayListCanvas c = node.start(width, height);
c.drawColor(color);
- decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets);
+ decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
node.end(c);
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
@@ -383,7 +383,7 @@
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
- topChild.getConfiguration().orientation, mainWindow.mStableInsets,
+ topChild.getConfiguration().orientation, mainWindow.getStableInsets(),
ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */,
false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
false);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 228bfad..9381fc6 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -18,20 +18,27 @@
import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS;
import static com.android.server.wm.WindowFramesProto.CUTOUT;
import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSETS;
import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_INSETS;
import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.STABLE_INSETS;
import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
+import android.view.WindowManager;
+import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -60,7 +67,7 @@
*
* TODO(b/111611553): The name is unclear and most likely should be swapped with
* {@link #mParentFrame}
- */
+ */
public final Rect mDisplayFrame = new Rect();
/**
@@ -118,6 +125,12 @@
*/
final Rect mLastFrame = new Rect();
+ private boolean mFrameSizeChanged = false;
+
+ // Frame that is scaled to the application's coordinate space when in
+ // screen size compatibility mode.
+ final Rect mCompatFrame = new Rect();
+
/**
* Whether the parent frame would have been different if there was no display cutout.
*/
@@ -131,7 +144,52 @@
/**
* The last cutout that has been reported to the client.
*/
- WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+ private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+ private boolean mDisplayCutoutChanged;
+
+ /**
+ * Insets that determine the area covered by the display overscan region. These are in the
+ * application's coordinate space (without compatibility scale applied).
+ */
+ final Rect mOverscanInsets = new Rect();
+ final Rect mLastOverscanInsets = new Rect();
+ private boolean mOverscanInsetsChanged;
+
+ /**
+ * Insets that determine the area covered by the stable system windows. These are in the
+ * application's coordinate space (without compatibility scale applied).
+ */
+ final Rect mStableInsets = new Rect();
+ final Rect mLastStableInsets = new Rect();
+ private boolean mStableInsetsChanged;
+
+ /**
+ * Outsets determine the area outside of the surface where we want to pretend that it's possible
+ * to draw anyway.
+ */
+ final Rect mOutsets = new Rect();
+ final Rect mLastOutsets = new Rect();
+ private boolean mOutsetsChanged = false;
+
+ /**
+ * Insets that determine the actually visible area. These are in the application's
+ * coordinate space (without compatibility scale applied).
+ */
+ final Rect mVisibleInsets = new Rect();
+ final Rect mLastVisibleInsets = new Rect();
+ private boolean mVisibleInsetsChanged;
+
+ /**
+ * Insets that are covered by system windows (such as the status bar) and
+ * transient docking windows (such as the IME). These are in the application's
+ * coordinate space (without compatibility scale applied).
+ */
+ final Rect mContentInsets = new Rect();
+ final Rect mLastContentInsets = new Rect();
+ private boolean mContentInsetsChanged;
+
+ private final Rect mTmpRect = new Rect();
public WindowFrames() {
}
@@ -171,15 +229,141 @@
/**
* @return true if the width or height has changed since last reported to the client.
*/
- boolean didFrameSizeChange() {
+ private boolean didFrameSizeChange() {
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
}
/**
- * @return true if the display cutout has changed since last reported to the client.
+ * Calculates the outsets for this windowFrame. The outsets are calculated by the area between
+ * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
+ * {@link #mOutsets} is set to empty.
+ *
+ * @param hasOutsets Whether this frame has outsets.
*/
- boolean didDisplayCutoutChange() {
- return !mLastDisplayCutout.equals(mDisplayCutout);
+ void calculateOutsets(boolean hasOutsets) {
+ if (hasOutsets) {
+ InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
+ } else {
+ mOutsets.setEmpty();
+ }
+ }
+
+ /**
+ * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+ *
+ * @param cutoutInsets The insets for the cutout.
+ */
+ void calculateDockedDividerInsets(Rect cutoutInsets) {
+ // For the docked divider, we calculate the stable insets like a full-screen window
+ // so it can use it to calculate the snap positions.
+ mTmpRect.set(mDisplayFrame);
+ mTmpRect.inset(cutoutInsets);
+ mTmpRect.intersectUnchecked(mStableFrame);
+ InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets);
+
+ // The divider doesn't care about insets in any case, so set it to empty so we don't
+ // trigger a relayout when moving it.
+ mContentInsets.setEmpty();
+ mVisibleInsets.setEmpty();
+ mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+ }
+
+ /**
+ * Calculate the insets for a window.
+ *
+ * @param windowsAreFloating Whether the window is in a floating task such as pinned or
+ * freeform
+ * @param inFullscreenContainer Whether the window is in a container that takes up the screen's
+ * entire space
+ * @param windowBounds The bounds for the window
+ */
+ void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer,
+ Rect windowBounds) {
+ // Override right and/or bottom insets in case if the frame doesn't fit the screen in
+ // non-fullscreen mode.
+ boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.right > windowBounds.right;
+ boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.bottom > windowBounds.bottom;
+
+ mTmpRect.set(mFrame.left, mFrame.top, overrideRightInset ? mTmpRect.right : mFrame.right,
+ overrideBottomInset ? mTmpRect.bottom : mFrame.bottom);
+
+ InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets);
+ InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets);
+ InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets);
+ }
+
+ /**
+ * Scales all the insets by a specific amount.
+ *
+ * @param scale The amount to scale the insets by.
+ */
+ void scaleInsets(float scale) {
+ mOverscanInsets.scale(scale);
+ mContentInsets.scale(scale);
+ mVisibleInsets.scale(scale);
+ mStableInsets.scale(scale);
+ mOutsets.scale(scale);
+ }
+
+ void offsetFrames(int layoutXDiff, int layoutYDiff) {
+ mFrame.offset(layoutXDiff, layoutYDiff);
+ mContentFrame.offset(layoutXDiff, layoutYDiff);
+ mVisibleFrame.offset(layoutXDiff, layoutYDiff);
+ mStableFrame.offset(layoutXDiff, layoutYDiff);
+ }
+
+ /**
+ * Updates info about whether the size of the window has changed since last reported.
+ *
+ * @return true if info about size has changed since last reported.
+ */
+ boolean setReportResizeHints() {
+ mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
+ mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
+ mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+ mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
+ mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
+ mFrameSizeChanged |= didFrameSizeChange();
+ mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
+ return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
+ || mStableInsetsChanged || mOutsetsChanged || mFrameSizeChanged
+ || mDisplayCutoutChanged;
+ }
+
+ /**
+ * Resets the insets changed flags so they're all set to false again. This should be called
+ * after the insets are reported to client.
+ */
+ void resetInsetsChanged() {
+ mOverscanInsetsChanged = false;
+ mContentInsetsChanged = false;
+ mVisibleInsetsChanged = false;
+ mStableInsetsChanged = false;
+ mOutsetsChanged = false;
+ mFrameSizeChanged = false;
+ mDisplayCutoutChanged = false;
+ }
+
+ /**
+ * Copy over inset values as the last insets that were sent to the client.
+ */
+ void updateLastInsetValues() {
+ mLastOverscanInsets.set(mOverscanInsets);
+ mLastContentInsets.set(mContentInsets);
+ mLastVisibleInsets.set(mVisibleInsets);
+ mLastStableInsets.set(mStableInsets);
+ mLastOutsets.set(mOutsets);
+ mLastDisplayCutout = mDisplayCutout;
+ }
+
+ /**
+ * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update
+ * the client.
+ */
+ void resetLastContentInsets() {
+ mLastContentInsets.set(-1, -1, -1, -1);
}
public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
@@ -194,6 +378,12 @@
mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
mFrame.writeToProto(proto, FRAME);
mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
+ mContentInsets.writeToProto(proto, CONTENT_INSETS);
+ mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
+ mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
+ mStableInsets.writeToProto(proto, STABLE_INSETS);
+ mOutsets.writeToProto(proto, OUTSETS);
+
proto.end(token);
}
@@ -211,5 +401,34 @@
+ " last=" + mLastFrame.toShortString(sTmpSB));
pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout()
+ " last=" + mLastDisplayCutout.getDisplayCutout());
+ pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB)
+ + " content=" + mContentInsets.toShortString(sTmpSB)
+ + " visible=" + mVisibleInsets.toShortString(sTmpSB)
+ + " stable=" + mStableInsets.toShortString(sTmpSB)
+ + " outsets=" + mOutsets.toShortString(sTmpSB));
+ pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB)
+ + " content=" + mLastContentInsets.toShortString(sTmpSB)
+ + " visible=" + mLastVisibleInsets.toShortString(sTmpSB)
+ + " stable=" + mLastStableInsets.toShortString(sTmpSB)
+ + " outset=" + mLastOutsets.toShortString(sTmpSB));
+ }
+
+ String getInsetsInfo() {
+ return "ci=" + mContentInsets.toShortString()
+ + " vi=" + mVisibleInsets.toShortString()
+ + " si=" + mStableInsets.toShortString()
+ + " of=" + mOutsets.toShortString();
+ }
+
+ String getInsetsChangedInfo() {
+ return "contentInsetsChanged=" + mContentInsetsChanged
+ + " " + mContentInsets.toShortString()
+ + " visibleInsetsChanged=" + mVisibleInsetsChanged
+ + " " + mVisibleInsets.toShortString()
+ + " stableInsetsChanged=" + mStableInsetsChanged
+ + " " + mStableInsets.toShortString()
+ + " outsetsChanged=" + mOutsetsChanged
+ + " " + mOutsets.toShortString()
+ + " displayCutoutChanged=" + mDisplayCutoutChanged;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e80a47e..679e0d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -734,8 +734,9 @@
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
- // Indicates whether this device supports wide color gamut rendering
+ // Indicates whether this device supports wide color gamut / HDR rendering
private boolean mHasWideColorGamutSupport;
+ private boolean mHasHdrSupport;
// Who is holding the screen on.
private Session mHoldingScreenOn;
@@ -2167,14 +2168,10 @@
// The last inset values represent the last client state.
win.updateLastInsetValues();
- outFrame.set(win.mCompatFrame);
- outOverscanInsets.set(win.mOverscanInsets);
- outContentInsets.set(win.mContentInsets);
- win.mLastRelayoutContentInsets.set(win.mContentInsets);
- outVisibleInsets.set(win.mVisibleInsets);
- outStableInsets.set(win.mStableInsets);
+ win.getCompatFrame(outFrame);
+ win.getInsetsForRelayout(outOverscanInsets, outContentInsets, outVisibleInsets,
+ outStableInsets, outOutsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
- outOutsets.set(win.mOutsets);
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
@@ -4504,6 +4501,7 @@
mPolicy.systemReady();
mTaskSnapshotController.systemReady();
mHasWideColorGamutSupport = queryWideColorGamutSupport();
+ mHasHdrSupport = queryHdrSupport();
}
private static boolean queryWideColorGamutSupport() {
@@ -4519,6 +4517,19 @@
return false;
}
+ private static boolean queryHdrSupport() {
+ try {
+ ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
+ OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay();
+ if (hasHdr != null) {
+ return hasHdr.value;
+ }
+ } catch (RemoteException e) {
+ // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
+ }
+ return false;
+ }
+
// -------------------------------------------------------------
// Async Handler
// -------------------------------------------------------------
@@ -5993,11 +6004,10 @@
}
@Override
- public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
+ public void createInputConsumer(IBinder token, String name, int displayId,
+ InputChannel inputChannel) {
synchronized (mWindowMap) {
- // TODO(b/112049699): Fix this for multiple displays. There is only one inputChannel
- // here to accept the return value.
- DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
display.getInputMonitor().createInputConsumer(token, name, inputChannel,
Binder.getCallingPid(), Binder.getCallingUserHandle());
@@ -6006,11 +6016,9 @@
}
@Override
- public boolean destroyInputConsumer(String name) {
+ public boolean destroyInputConsumer(String name, int displayId) {
synchronized (mWindowMap) {
- // TODO(b/112049699): Fix this for multiple displays. For consistency with
- // createInputConsumer above.
- DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
return display.getInputMonitor().destroyInputConsumer(name);
}
@@ -7499,6 +7507,10 @@
SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1;
}
+ boolean hasHdrSupport() {
+ return mHasHdrSupport && hasWideColorGamutSupport();
+ }
+
void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
if (!win.hideNonSystemOverlayWindowsWhenVisible()
&& !mHidingNonSystemOverlayWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 466e298..637c0ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -120,7 +120,6 @@
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.DESTROYING;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
@@ -131,20 +130,16 @@
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
-import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.WindowStateProto.STABLE_INSETS;
import static com.android.server.wm.WindowStateProto.STACK_ID;
import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
@@ -200,6 +195,7 @@
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -309,22 +305,6 @@
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
- * Insets that determine the actually visible area. These are in the application's
- * coordinate space (without compatibility scale applied).
- */
- final Rect mVisibleInsets = new Rect();
- private final Rect mLastVisibleInsets = new Rect();
- private boolean mVisibleInsetsChanged;
-
- /**
- * Insets that are covered by system windows (such as the status bar) and
- * transient docking windows (such as the IME). These are in the application's
- * coordinate space (without compatibility scale applied).
- */
- final Rect mContentInsets = new Rect();
- final Rect mLastContentInsets = new Rect();
-
- /**
* The last content insets returned to the client in relayout. We use
* these in the bounds animation to ensure we only observe inset changes
* at the same time that a client resizes it's surface so that we may use
@@ -333,34 +313,6 @@
*/
final Rect mLastRelayoutContentInsets = new Rect();
- private boolean mContentInsetsChanged;
-
- /**
- * Insets that determine the area covered by the display overscan region. These are in the
- * application's coordinate space (without compatibility scale applied).
- */
- final Rect mOverscanInsets = new Rect();
- private final Rect mLastOverscanInsets = new Rect();
- private boolean mOverscanInsetsChanged;
-
- /**
- * Insets that determine the area covered by the stable system windows. These are in the
- * application's coordinate space (without compatibility scale applied).
- */
- final Rect mStableInsets = new Rect();
- private final Rect mLastStableInsets = new Rect();
- private boolean mStableInsetsChanged;
-
- /**
- * Outsets determine the area outside of the surface where we want to pretend that it's possible
- * to draw anyway.
- */
- final Rect mOutsets = new Rect();
- private final Rect mLastOutsets = new Rect();
- private boolean mOutsetsChanged = false;
-
- private boolean mDisplayCutoutChanged;
-
/**
* Set to true if we are waiting for this window to receive its
* given internal insets before laying out other windows based on it.
@@ -399,11 +351,6 @@
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
- private boolean mFrameSizeChanged = false;
- // Frame that is scaled to the application's coordinate space when in
- // screen size compatibility mode.
- final Rect mCompatFrame = new Rect();
-
private final WindowFrames mWindowFrames = new WindowFrames();
/**
@@ -988,17 +935,7 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
- if (hasOutsets) {
- mOutsets.set(
- Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
- Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
- Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
- 0),
- Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
- 0));
- } else {
- mOutsets.set(0, 0, 0, 0);
- }
+ mWindowFrames.calculateOutsets(hasOutsets);
// Make sure the content and visible frames are inside of the
// final window frame.
@@ -1055,90 +992,35 @@
if (inFullscreenContainer && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
- mOverscanInsets.set(
- Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
- Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
- Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
- Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
- 0));
+ InsetUtils.insetsBetweenFrames(layoutContainingFrame, mWindowFrames.mOverscanFrame,
+ mWindowFrames.mOverscanInsets);
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- // For the docked divider, we calculate the stable insets like a full-screen window
- // so it can use it to calculate the snap positions.
final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
mWindowFrames.mDisplayFrame);
- mTmpRect.set(mWindowFrames.mDisplayFrame);
- mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
- mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
-
- mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
- Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
- Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
- Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
-
- // The divider doesn't care about insets in any case, so set it to empty so we don't
- // trigger a relayout when moving it.
- mContentInsets.setEmpty();
- mVisibleInsets.setEmpty();
- windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT);
+ mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
} else {
getDisplayContent().getBounds(mTmpRect);
- // Override right and/or bottom insets in case if the frame doesn't fit the screen in
- // non-fullscreen mode.
- boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
- && mWindowFrames.mFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
- && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
- mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
- mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
- overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
- : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
- : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
-
- mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
- mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
- overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
- : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
- : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
-
- mStableInsets.set(
- Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
- Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
- overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
- 0) : Math.max(
- mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
- overrideBottomInset ? Math.max(
- mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
- mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
+ mWindowFrames.calculateInsets(windowsAreFloating, inFullscreenContainer, mTmpRect);
}
mWindowFrames.setDisplayCutout(
windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
- mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
- mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
- mCompatFrame.set(mWindowFrames.mFrame);
+ mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
// reported to the app in its coordinate space.
- mOverscanInsets.scale(mInvGlobalScale);
- mContentInsets.scale(mInvGlobalScale);
- mVisibleInsets.scale(mInvGlobalScale);
- mStableInsets.scale(mInvGlobalScale);
- mOutsets.scale(mInvGlobalScale);
+ mWindowFrames.scaleInsets(mInvGlobalScale);
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
- mCompatFrame.scale(mInvGlobalScale);
+ mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
}
if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
@@ -1156,10 +1038,7 @@
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ "): frame=" + mWindowFrames.mFrame.toShortString()
- + " ci=" + mContentInsets.toShortString()
- + " vi=" + mVisibleInsets.toShortString()
- + " si=" + mStableInsets.toShortString()
- + " of=" + mOutsets.toShortString());
+ + " " + mWindowFrames.getInsetsInfo());
}
// TODO: Look into whether this override is still necessary.
@@ -1219,6 +1098,14 @@
return mWindowFrames.mDisplayCutout;
}
+ void getCompatFrame(Rect outFrame) {
+ outFrame.set(mWindowFrames.mCompatFrame);
+ }
+
+ void getCompatFrameSize(Rect outFrame) {
+ outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
+ }
+
@Override
public boolean getGivenInsetsPendingLw() {
return mGivenInsetsPending;
@@ -1270,15 +1157,7 @@
}
boolean setReportResizeHints() {
- mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
- mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
- mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
- mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
- mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
- mFrameSizeChanged |= mWindowFrames.didFrameSizeChange();
- mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange();
- return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
- || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
+ return mWindowFrames.setReportResizeHints();
}
/**
@@ -1301,7 +1180,7 @@
return;
}
- setReportResizeHints();
+ boolean didFrameInsetsChange = setReportResizeHints();
boolean configChanged = isConfigChanged();
if (DEBUG_CONFIGURATION && configChanged) {
Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
@@ -1318,31 +1197,18 @@
// variables, because mFrameSizeChanged only tracks the width and height changing.
mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
- if (mContentInsetsChanged
- || mVisibleInsetsChanged
- || mStableInsetsChanged
+ if (didFrameInsetsChange
|| winAnimator.mSurfaceResized
- || mOutsetsChanged
- || mFrameSizeChanged
- || mDisplayCutoutChanged
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
- + " contentInsetsChanged=" + mContentInsetsChanged
- + " " + mContentInsets.toShortString()
- + " visibleInsetsChanged=" + mVisibleInsetsChanged
- + " " + mVisibleInsets.toShortString()
- + " stableInsetsChanged=" + mStableInsetsChanged
- + " " + mStableInsets.toShortString()
- + " outsetsChanged=" + mOutsetsChanged
- + " " + mOutsets.toShortString()
+ + " " + mWindowFrames.getInsetsChangedInfo()
+ " surfaceResized=" + winAnimator.mSurfaceResized
+ " configChanged=" + configChanged
+ " dragResizingChanged=" + dragResizingChanged
- + " reportOrientationChanged=" + mReportOrientationChanged
- + " displayCutoutChanged=" + mDisplayCutoutChanged);
+ + " reportOrientationChanged=" + mReportOrientationChanged);
}
// If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -3015,7 +2881,7 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
- + ": " + mCompatFrame);
+ + ": " + mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
new MergedConfiguration(mService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
@@ -3026,11 +2892,11 @@
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
final Rect frame = mWindowFrames.mFrame;
- final Rect overscanInsets = mLastOverscanInsets;
- final Rect contentInsets = mLastContentInsets;
- final Rect visibleInsets = mLastVisibleInsets;
- final Rect stableInsets = mLastStableInsets;
- final Rect outsets = mLastOutsets;
+ final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
+ final Rect contentInsets = mWindowFrames.mLastContentInsets;
+ final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
+ final Rect stableInsets = mWindowFrames.mLastStableInsets;
+ final Rect outsets = mWindowFrames.mLastOutsets;
final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
final boolean reportOrientation = mReportOrientationChanged;
final int displayId = getDisplayId();
@@ -3061,13 +2927,7 @@
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- mOverscanInsetsChanged = false;
- mContentInsetsChanged = false;
- mVisibleInsetsChanged = false;
- mStableInsetsChanged = false;
- mOutsetsChanged = false;
- mFrameSizeChanged = false;
- mDisplayCutoutChanged = false;
+ mWindowFrames.resetInsetsChanged();
mWinAnimator.mSurfaceResized = false;
mReportOrientationChanged = false;
} catch (RemoteException e) {
@@ -3293,7 +3153,6 @@
mAttrs.writeToProto(proto, ATTRIBUTES);
mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
- mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
@@ -3307,10 +3166,6 @@
proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
proto.write(HAS_SURFACE, mHasSurface);
proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
- mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
- mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
- mStableInsets.writeToProto(proto, STABLE_INSETS);
- mOutsets.writeToProto(proto, OUTSETS);
proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
proto.write(DESTROYING, mDestroying);
proto.write(REMOVED, mRemoved);
@@ -3417,21 +3272,11 @@
+ " isReadyForDisplay()=" + isReadyForDisplay()
+ " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
if (mEnforceSizeCompat) {
- pw.println(prefix + "mCompatFrame=" + mCompatFrame.toShortString(sTmpSB));
+ pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
}
if (dumpAll) {
mWindowFrames.dump(pw, prefix);
- pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB)
- + " content=" + mContentInsets.toShortString(sTmpSB)
- + " visible=" + mVisibleInsets.toShortString(sTmpSB)
- + " stable=" + mStableInsets.toShortString(sTmpSB)
- + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB)
- + " outsets=" + mOutsets.toShortString(sTmpSB));
- pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB)
- + " content=" + mLastContentInsets.toShortString(sTmpSB)
- + " visible=" + mLastVisibleInsets.toShortString(sTmpSB)
- + " stable=" + mLastStableInsets.toShortString(sTmpSB)
- + " outset=" + mLastOutsets.toShortString(sTmpSB));
+ pw.println(prefix + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB));
}
super.dump(pw, prefix, dumpAll);
pw.println(prefix + mWinAnimator + ":");
@@ -3531,7 +3376,7 @@
}
}
- void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
+ private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
@@ -3609,10 +3454,10 @@
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
- mCompatFrame.set(mWindowFrames.mFrame);
+ mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// See comparable block in computeFrameLw.
- mCompatFrame.scale(mInvGlobalScale);
+ mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
}
}
@@ -4322,13 +4167,15 @@
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
// TODO(multi-display)
- policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
- policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
- displayInfo.logicalWidth - mCompatFrame.left,
- displayInfo.logicalHeight - mCompatFrame.top);
+ policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
+ mWindowFrames.mCompatFrame.height());
+ policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
+ displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left,
+ displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top);
} else if (skipDecorCrop()) {
// Windows without policy decor aren't cropped.
- policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
+ policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
+ mWindowFrames.mCompatFrame.height());
} else {
// Crop to the system decor specified by policy.
calculateSystemDecorRect(policyCrop);
@@ -4486,12 +4333,7 @@
* Updates the last inset values to the current ones.
*/
void updateLastInsetValues() {
- mLastOverscanInsets.set(mOverscanInsets);
- mLastContentInsets.set(mContentInsets);
- mLastVisibleInsets.set(mVisibleInsets);
- mLastStableInsets.set(mStableInsets);
- mLastOutsets.set(mOutsets);
- mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout;
+ mWindowFrames.updateLastInsetValues();
}
void startAnimation(Animation anim) {
@@ -4880,6 +4722,44 @@
}
}
+ /**
+ * Copy the inset values over so they can be sent back to the client when a relayout occurs.
+ */
+ void getInsetsForRelayout(Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+ Rect outStableInsets, Rect outOutsets) {
+ outOverscanInsets.set(mWindowFrames.mOverscanInsets);
+ outContentInsets.set(mWindowFrames.mContentInsets);
+ outVisibleInsets.set(mWindowFrames.mVisibleInsets);
+ outStableInsets.set(mWindowFrames.mStableInsets);
+ outOutsets.set(mWindowFrames.mOutsets);
+
+ mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
+ }
+
+ void getContentInsets(Rect outContentInsets) {
+ outContentInsets.set(mWindowFrames.mContentInsets);
+ }
+
+ Rect getContentInsets() {
+ return mWindowFrames.mContentInsets;
+ }
+
+ void getStableInsets(Rect outStableInsets) {
+ outStableInsets.set(mWindowFrames.mStableInsets);
+ }
+
+ Rect getStableInsets() {
+ return mWindowFrames.mStableInsets;
+ }
+
+ void resetLastContentInsets() {
+ mWindowFrames.resetLastContentInsets();
+ }
+
+ Rect getVisibleInsets() {
+ return mWindowFrames.mVisibleInsets;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b158ae2..c80eb86 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_NONE;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
@@ -46,7 +47,6 @@
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import android.content.Context;
import android.graphics.Matrix;
@@ -476,8 +476,7 @@
flags |= SurfaceControl.SECURE;
}
- mTmpSize.set(0, 0, 0, 0);
- calculateSurfaceBounds(w, attrs);
+ calculateSurfaceBounds(w, attrs, mTmpSize);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -556,44 +555,38 @@
return mSurfaceController;
}
- private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) {
+ private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) {
+ outSize.setEmpty();
if ((attrs.flags & FLAG_SCALED) != 0) {
// For a scaled surface, we always want the requested size.
- mTmpSize.right = mTmpSize.left + w.mRequestedWidth;
- mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight;
+ outSize.right = w.mRequestedWidth;
+ outSize.bottom = w.mRequestedHeight;
} else {
// When we're doing a drag-resizing, request a surface that's fullscreen size,
// so that we don't need to reallocate during the process. This also prevents
// buffer drops due to size mismatch.
if (w.isDragResizing()) {
- if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
- mTmpSize.left = 0;
- mTmpSize.top = 0;
- }
final DisplayInfo displayInfo = w.getDisplayInfo();
- mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth;
- mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight;
+ outSize.right = displayInfo.logicalWidth;
+ outSize.bottom = displayInfo.logicalHeight;
} else {
- mTmpSize.right = mTmpSize.left + w.mCompatFrame.width();
- mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height();
+ w.getCompatFrameSize(outSize);
}
}
// Something is wrong and SurfaceFlinger will not like this, try to revert to sane values.
// This doesn't necessarily mean that there is an error in the system. The sizes might be
// incorrect, because it is before the first layout or draw.
- if (mTmpSize.width() < 1) {
- mTmpSize.right = mTmpSize.left + 1;
+ if (outSize.width() < 1) {
+ outSize.right = 1;
}
- if (mTmpSize.height() < 1) {
- mTmpSize.bottom = mTmpSize.top + 1;
+ if (outSize.height() < 1) {
+ outSize.bottom = 1;
}
// Adjust for surface insets.
- mTmpSize.left -= attrs.surfaceInsets.left;
- mTmpSize.top -= attrs.surfaceInsets.top;
- mTmpSize.right += attrs.surfaceInsets.right;
- mTmpSize.bottom += attrs.surfaceInsets.bottom;
+ outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
+ -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
}
boolean hasSurface() {
@@ -870,8 +863,7 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- mTmpSize.set(0, 0, 0, 0);
- calculateSurfaceBounds(w, attrs);
+ calculateSurfaceBounds(w, attrs, mTmpSize);
mExtraHScale = (float) 1.0;
mExtraVScale = (float) 1.0;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b467d61..42ade38 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -210,7 +210,7 @@
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
- void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray);
+ void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
@@ -736,7 +736,8 @@
}
}
-void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray,
+ int32_t displayId) {
Vector<sp<InputWindowHandle> > windowHandles;
if (windowHandleObjArray) {
@@ -756,7 +757,7 @@
}
}
- mInputManager->getDispatcher()->setInputWindows(windowHandles);
+ mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId);
// Do this after the dispatcher has updated the window handle state.
bool newPointerGesturesEnabled = true;
@@ -1446,10 +1447,10 @@
}
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobjectArray windowHandleObjArray) {
+ jlong ptr, jobjectArray windowHandleObjArray, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setInputWindows(env, windowHandleObjArray);
+ im->setInputWindows(env, windowHandleObjArray, displayId);
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
@@ -1678,7 +1679,7 @@
(void*) nativeInjectInputEvent },
{ "nativeToggleCapsLock", "(JI)V",
(void*) nativeToggleCapsLock },
- { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
+ { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
(void*) nativeSetInputWindows },
{ "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
(void*) nativeSetFocusedApplication },
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ecc13b2..b9f8fdb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -743,10 +743,20 @@
traceEnd();
}
+ // Tracks and caches the device state.
+ traceBeginAndSlog("StartCachedDeviceStateService");
+ mSystemServiceManager.startService(CachedDeviceStateService.class);
+ traceEnd();
+
// Tracks cpu time spent in binder calls
traceBeginAndSlog("StartBinderCallsStatsService");
mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
traceEnd();
+
+ // Tracks time spent in handling messages in handlers.
+ traceBeginAndSlog("StartLooperStatsService");
+ mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);
+ traceEnd();
}
/**
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 3cdef1e..ccc092d 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -1385,6 +1385,20 @@
private boolean startIpReachabilityMonitor() {
try {
+ // TODO: Fetch these parameters from settings, and install a
+ // settings observer to watch for update and re-program these
+ // parameters (Q: is this level of dynamic updatability really
+ // necessary or does reading from settings at startup suffice?).
+ final int NUM_SOLICITS = 5;
+ final int INTER_SOLICIT_INTERVAL_MS = 750;
+ setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
+ NUM_SOLICITS, INTER_SOLICIT_INTERVAL_MS);
+ } catch (Exception e) {
+ mLog.e("Failed to adjust neighbor parameters", e);
+ // Carry on using the system defaults (currently: 3, 1000);
+ }
+
+ try {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
mInterfaceParams,
@@ -1863,6 +1877,20 @@
}
}
+ private static void setNeighborParameters(
+ INetd netd, String ifName, int num_solicits, int inter_solicit_interval_ms)
+ throws RemoteException, IllegalArgumentException {
+ Preconditions.checkNotNull(netd);
+ Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
+ Preconditions.checkArgument(num_solicits > 0);
+ Preconditions.checkArgument(inter_solicit_interval_ms > 0);
+
+ for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", Integer.toString(inter_solicit_interval_ms));
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", Integer.toString(num_solicits));
+ }
+ }
+
// TODO: extract out into CollectionUtils.
static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
for (T t : coll) {
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
new file mode 100644
index 0000000..34de9df
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ services.core \
+ services.devicepolicy \
+ frameworks-base-testutils \
+ androidx-test \
+ mockito-target-extended-minus-junit4 \
+ ShortcutManagerTestUtils \
+ compatibility-device-util \
+ truth-prebuilt
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.mock
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+ libstaticjvmtiagent
+
+LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
new file mode 100644
index 0000000..247e446
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.mockingservicestests">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.mockingservicestests"
+ android:label="Frameworks Mocking Services Tests" />
+</manifest>
diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml
new file mode 100644
index 0000000..adfee96
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="FrameworksMockingServicesTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.mockingservicestests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 910aad7f..c8e6782 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -64,9 +64,6 @@
import android.util.ArraySet;
import android.util.Pair;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.server.AppStateTracker.Listener;
@@ -88,11 +85,14 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Tests for {@link AppStateTracker}
*
* Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -683,10 +683,10 @@
//--------------------------------------------------
List<OpEntry> entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -694,7 +694,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -702,7 +702,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
@@ -710,10 +710,10 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -754,7 +754,7 @@
final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+ Listener l = mock(Listener.class);
instance.addListener(l);
// Power save on.
@@ -797,7 +797,7 @@
final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+ Listener l = mock(Listener.class);
instance.addListener(l);
// -------------------------------------------------------------------------
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
new file mode 100644
index 0000000..81107cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 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.server;
+
+
+import static org.mockito.Mockito.when;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.IPowerManager;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.os.RemoteException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CachedDeviceStateService}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CachedDeviceStateServiceTest {
+ @Mock private BatteryManagerInternal mBatteryManager;
+ @Mock private IPowerManager mPowerManager;
+ private BroadcastInterceptingContext mContext;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getContext();
+ PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+ mContext = new BroadcastInterceptingContext(context) {
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.POWER_SERVICE:
+ return powerManager;
+ default:
+ return super.getSystemService(name);
+ }
+ }
+ };
+
+ LocalServices.addService(BatteryManagerInternal.class, mBatteryManager);
+
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() {
+ // Added by the CachedDeviceStateService.onStart().
+ LocalServices.removeServiceForTest(CachedDeviceState.Readonly.class);
+
+ // Added in @Before.
+ LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+ }
+
+ @Test
+ public void correctlyReportsScreenInteractive() throws RemoteException {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mPowerManager.isInteractive()).thenReturn(true); // Screen on.
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(deviceState.isScreenInteractive()).isFalse();
+
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(deviceState.isScreenInteractive()).isTrue();
+
+ mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+ assertThat(deviceState.isScreenInteractive()).isFalse();
+
+ mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+ assertThat(deviceState.isScreenInteractive()).isTrue();
+ }
+
+ @Test
+ public void correctlyReportsCharging() {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(deviceState.isCharging()).isTrue();
+
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(deviceState.isCharging()).isFalse();
+
+ Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ mContext.sendBroadcast(intentPluggedIn);
+ assertThat(deviceState.isCharging()).isTrue();
+
+ Intent intentUnplugged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentUnplugged.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+ mContext.sendBroadcast(intentUnplugged);
+ assertThat(deviceState.isCharging()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index ea3a3d0..e648230 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -133,15 +133,15 @@
}
private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mContentInsets, left, top, right, bottom);
+ assertRect(w.getContentInsets(), left, top, right, bottom);
}
private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mVisibleInsets, left, top, right, bottom);
+ assertRect(w.getVisibleInsets(), left, top, right, bottom);
}
private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mStableInsets, left, top, right, bottom);
+ assertRect(w.getStableInsets(), left, top, right, bottom);
}
private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 2f1c516..4874bce 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -44,6 +44,10 @@
private static final String TAG = UsbAlsaManager.class.getSimpleName();
private static final boolean DEBUG = false;
+ // Flag to turn on/off multi-peripheral select mode
+ // Set to true to have single-device-only mode
+ private static final boolean mIsSingleMode = true;
+
private static final String ALSA_DIRECTORY = "/dev/snd/";
private final Context mContext;
@@ -84,10 +88,11 @@
*/
private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) {
if (DEBUG) {
- Slog.d(TAG, "selectAlsaDevice " + alsaDevice);
+ Slog.d(TAG, "selectAlsaDevice() " + alsaDevice);
}
- if (mSelectedDevice != null) {
+ // This must be where an existing USB audio device is deselected.... (I think)
+ if (mIsSingleMode && mSelectedDevice != null) {
deselectAlsaDevice();
}
@@ -104,9 +109,15 @@
mSelectedDevice = alsaDevice;
alsaDevice.start();
+ if (DEBUG) {
+ Slog.d(TAG, "selectAlsaDevice() - done.");
+ }
}
private synchronized void deselectAlsaDevice() {
+ if (DEBUG) {
+ Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice);
+ }
if (mSelectedDevice != null) {
mSelectedDevice.stop();
mSelectedDevice = null;
@@ -133,7 +144,7 @@
/* package */ UsbAlsaDevice selectDefaultDevice() {
if (DEBUG) {
- Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
+ Slog.d(TAG, "selectDefaultDevice()");
}
if (mAlsaDevices.size() > 0) {
@@ -230,6 +241,8 @@
}
}
+ logDevices("deviceAdded()");
+
if (DEBUG) {
Slog.d(TAG, "deviceAdded() - done");
}
@@ -254,6 +267,9 @@
Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
IoUtils.closeQuietly(usbMidiDevice);
}
+
+ logDevices("usbDeviceRemoved()");
+
}
/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
@@ -296,6 +312,7 @@
/**
* Dump the USB alsa state.
*/
+ // invoked with "adb shell dumpsys usb"
public void dump(DualDumpOutputStream dump, String idName, long id) {
long token = dump.start(idName, id);
@@ -314,29 +331,26 @@
dump.end(token);
}
-/*
public void logDevicesList(String title) {
- if (DEBUG) {
- for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
- Slog.i(TAG, "UsbDevice-------------------");
- Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
- Slog.i(TAG, "UsbAlsaDevice--------------");
- Slog.i(TAG, "" + entry.getValue());
- }
- }
+ if (DEBUG) {
+ Slog.i(TAG, title + "----------------");
+ for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+ Slog.i(TAG, " -->");
+ Slog.i(TAG, "" + alsaDevice);
+ Slog.i(TAG, " <--");
+ }
+ Slog.i(TAG, "----------------");
+ }
}
-*/
// This logs a more terse (and more readable) version of the devices list
-/*
public void logDevices(String title) {
- if (DEBUG) {
- Slog.i(TAG, title);
- for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
- Slog.i(TAG, entry.getValue().toShortString());
- }
- }
+ if (DEBUG) {
+ Slog.i(TAG, title + "----------------");
+ for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+ Slog.i(TAG, alsaDevice.toShortString());
+ }
+ Slog.i(TAG, "----------------");
+ }
}
-*/
-
}
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
new file mode 100644
index 0000000..1730d8f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2018 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.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * UsbResolveActivityManager creates UI dialogs for user to pick or confirm handler for
+ * usb attach event.
+ *
+ * @hide
+ */
+class UsbHandlerManager {
+ private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName();
+
+ private final Context mContext;
+
+ UsbHandlerManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Shows dialog to user to allow them to optionally visit that URL for more
+ * information or software downloads if the attached USB accessory has a valid
+ * URL associated with it.
+ *
+ * @param accessory The accessory to confirm in the UI dialog
+ * @param user The user to start the UI dialog
+ */
+ void showUsbAccessoryUriActivity(@NonNull UsbAccessory accessory,
+ @NonNull UserHandle user) {
+ String uri = accessory.getUri();
+ if (uri != null && uri.length() > 0) {
+ // display URI to user
+ Intent dialogIntent = createDialogIntent();
+ dialogIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbAccessoryUriActivity");
+ dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ dialogIntent.putExtra("uri", uri);
+ try {
+ mContext.startActivityAsUser(dialogIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start UsbAccessoryUriActivity");
+ }
+ }
+ }
+
+ /**
+ * Shows dialog to user to confirm the package to start when the USB device
+ * or accessory is attached and there is only one package claims to handle this
+ * USB device or accessory.
+ *
+ * @param rInfo The ResolveInfo of the package to confirm in the UI dialog
+ * @param device The USB device to confirm
+ * @param accessory The USB accessory to confirm
+ */
+ void confirmUsbHandler(@NonNull ResolveInfo rInfo, @Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory) {
+ Intent resolverIntent = createDialogIntent();
+ // start UsbConfirmActivity if there is only one choice
+ resolverIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbConfirmActivity");
+ resolverIntent.putExtra("rinfo", rInfo);
+ UserHandle user =
+ UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
+
+ if (device != null) {
+ resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ } else {
+ resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ }
+
+ try {
+ mContext.startActivityAsUser(resolverIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+ }
+ }
+
+ /**
+ * Shows dialog to user to select the package to start when the USB device
+ * or accessory is attached and there are more than one package claim to handle this
+ * USB device or accessory.
+ *
+ * @param matches The available resolutions of the intent
+ * @param user The user to start UI dialog
+ * @param intent The intent to start the UI dialog
+ */
+ void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
+ @NonNull UserHandle user, @NonNull Intent intent) {
+ Intent resolverIntent = createDialogIntent();
+ resolverIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbResolverActivity");
+ resolverIntent.putParcelableArrayListExtra("rlist", matches);
+ resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+
+ try {
+ mContext.startActivityAsUser(resolverIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+ }
+ }
+
+ private Intent createDialogIntent() {
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 67ad090..589bcdc 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -408,11 +408,15 @@
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
+ if (DEBUG) {
+ Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
+ }
+
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
- mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
+ mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
new file mode 100644
index 0000000..2c9ee36
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2018 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.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.HashMap;
+
+/**
+ * UsbPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbPermissionManager {
+ private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+
+ @GuardedBy("mLock")
+ /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
+ private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+ new HashMap<>();
+ @GuardedBy("mLock")
+ /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
+ private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+ new HashMap<>();
+
+ private final UserHandle mUser;
+ private final boolean mDisablePermissionDialogs;
+
+ private final Object mLock = new Object();
+
+ UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
+ mUser = user;
+ mDisablePermissionDialogs = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB accessory.
+ *
+ * @param accessory to remove permissions for
+ */
+ void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+ synchronized (mLock) {
+ mAccessoryPermissionMap.remove(accessory);
+ }
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB device.
+ *
+ * @param device to remove permissions for
+ */
+ void removeDevicePermissions(@NonNull UsbDevice device) {
+ synchronized (mLock) {
+ mDevicePermissionMap.remove(device.getDeviceName());
+ }
+ }
+
+ /**
+ * Grants permission for USB device without showing system dialog for package with uid.
+ *
+ * @param device to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+ synchronized (mLock) {
+ String deviceName = device.getDeviceName();
+ SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mDevicePermissionMap.put(deviceName, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Grants permission for USB accessory without showing system dialog for package with uid.
+ *
+ * @param accessory to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+ synchronized (mLock) {
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mAccessoryPermissionMap.put(accessory, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Returns true if package with uid has permission to access the device.
+ *
+ * @param device to check permission for
+ * @param uid to check permission for
+ * @return {@code true} if package with uid has permission
+ */
+ boolean hasPermission(@NonNull UsbDevice device, int uid) {
+ synchronized (mLock) {
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ /**
+ * Returns true if caller has permission to access the accessory.
+ *
+ * @param accessory to check permission for
+ * @return {@code true} if caller has permssion
+ */
+ boolean hasPermission(@NonNull UsbAccessory accessory) {
+ synchronized (mLock) {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ /**
+ * Creates UI dialog to request permission for the given package to access the device
+ * or accessory.
+ *
+ * @param device The USB device attached
+ * @param accessory The USB accessory attached
+ * @param canBeDefault Whether the calling pacakge can set as default handler
+ * of the USB device or accessory
+ * @param packageName The package name of the calling package
+ * @param uid The uid of the calling package
+ * @param userContext The context to start the UI dialog
+ * @param pi PendingIntent for returning result
+ */
+ void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ @NonNull String packageName,
+ int uid,
+ @NonNull Context userContext,
+ @NonNull PendingIntent pi) {
+ long identity = Binder.clearCallingIdentity();
+ Intent intent = new Intent();
+ if (device != null) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ } else {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ }
+ intent.putExtra(Intent.EXTRA_INTENT, pi);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+ intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbPermissionActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ userContext.startActivityAsUser(intent, mUser);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void dump(@NonNull DualDumpOutputStream dump) {
+ synchronized (mLock) {
+ for (String deviceName : mDevicePermissionMap.keySet()) {
+ long devicePermissionToken = dump.start("device_permissions",
+ UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+ dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
+ SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+ int count = uidList.size();
+ for (int i = 0; i < count; i++) {
+ dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+ }
+
+ dump.end(devicePermissionToken);
+ }
+
+ for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+ long accessoryPermissionToken = dump.start("accessory_permissions",
+ UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+ dump.write("accessory_description",
+ UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+ accessory.getDescription());
+
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ int count = uidList.size();
+ for (int i = 0; i < count; i++) {
+ dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+ }
+
+ dump.end(accessoryPermissionToken);
+ }
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 7a906d0..1ab1f7e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -193,6 +193,8 @@
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final UsbHandlerManager mUsbHandlerManager;
+
private final MtpNotificationManager mMtpNotificationManager;
/**
@@ -201,9 +203,11 @@
* @param context The context of the service
* @param user The parent profile
* @param settingsManager The settings manager of the service
+ * @param usbResolveActivityManager The resovle activity manager of the service
*/
UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
- @NonNull UsbSettingsManager settingsManager) {
+ @NonNull UsbSettingsManager settingsManager,
+ @NonNull UsbHandlerManager usbResolveActivityManager) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
Context parentUserContext;
@@ -238,6 +242,8 @@
parentUserContext,
device -> resolveActivity(createDeviceAttachedIntent(device),
device, false /* showMtpNotification */));
+
+ mUsbHandlerManager = usbResolveActivityManager;
}
/**
@@ -830,23 +836,8 @@
// don't show the resolver activity if there are no choices available
if (matches.size() == 0) {
if (accessory != null) {
- String uri = accessory.getUri();
- if (uri != null && uri.length() > 0) {
- // display URI to user
- Intent dialogIntent = new Intent();
- dialogIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbAccessoryUriActivity");
- dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- dialogIntent.putExtra("uri", uri);
- try {
- mContext.startActivityAsUser(dialogIntent, mParentUser);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
- }
- }
+ mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser);
}
-
// do nothing
return;
}
@@ -875,37 +866,10 @@
Slog.e(TAG, "startActivity failed", e);
}
} else {
- Intent resolverIntent = new Intent();
- resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- UserHandle user;
-
if (matches.size() == 1) {
- ResolveInfo rInfo = matches.get(0);
-
- // start UsbConfirmActivity if there is only one choice
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbConfirmActivity");
- resolverIntent.putExtra("rinfo", rInfo);
- user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
-
- if (device != null) {
- resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
- } else {
- resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- }
+ mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory);
} else {
- user = mParentUser;
-
- // start UsbResolverActivity so user can choose an activity
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbResolverActivity");
- resolverIntent.putParcelableArrayListExtra("rlist", matches);
- resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
- }
- try {
- mContext.startActivityAsUser(resolverIntent, user);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start activity " + resolverIntent, e);
+ mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent);
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 9221825..27566f0 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -57,10 +57,12 @@
private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup
= new SparseArray<>();
private UserManager mUserManager;
+ private UsbHandlerManager mUsbHandlerManager;
public UsbSettingsManager(@NonNull Context context) {
mContext = context;
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mUsbHandlerManager = new UsbHandlerManager(context);
}
/**
@@ -74,7 +76,8 @@
synchronized (mSettingsByUser) {
UsbUserSettingsManager settings = mSettingsByUser.get(userId);
if (settings == null) {
- settings = new UsbUserSettingsManager(mContext, new UserHandle(userId));
+ settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
+ new UsbPermissionManager(mContext, UserHandle.of(userId)));
mSettingsByUser.put(userId, settings);
}
return settings;
@@ -102,7 +105,8 @@
UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get(
parentUser.getIdentifier());
if (settings == null) {
- settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this);
+ settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this,
+ mUsbHandlerManager);
mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings);
}
return settings;
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 24a2d72..fe93399 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,15 +21,18 @@
import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
import android.hardware.usb.AccessoryFilter;
import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
@@ -38,42 +41,34 @@
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Binder;
-import android.os.Process;
import android.os.UserHandle;
import android.service.usb.UsbAccessoryAttachedActivities;
import android.service.usb.UsbDeviceAttachedActivities;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
import android.service.usb.UsbUserSettingsManagerProto;
import android.util.Slog;
-import android.util.SparseBooleanArray;
+import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
+import org.xmlpull.v1.XmlPullParser;
+
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
class UsbUserSettingsManager {
- private static final String TAG = "UsbUserSettingsManager";
+ private static final String TAG = UsbUserSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
private final UserHandle mUser;
- private final boolean mDisablePermissionDialogs;
private final Context mUserContext;
private final PackageManager mPackageManager;
-
- // Temporary mapping USB device name to list of UIDs with permissions for the device
- private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
- new HashMap<>();
- // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
- private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
- new HashMap<>();
+ private final UsbPermissionManager mUsbPermissionManager;
private final Object mLock = new Object();
- public UsbUserSettingsManager(Context context, UserHandle user) {
+ UsbUserSettingsManager(Context context, UserHandle user,
+ @NonNull UsbPermissionManager usbPermissionManager) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
try {
@@ -85,9 +80,7 @@
mPackageManager = mUserContext.getPackageManager();
mUser = user;
-
- mDisablePermissionDialogs = context.getResources().getBoolean(
- com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+ mUsbPermissionManager = usbPermissionManager;
}
/**
@@ -96,9 +89,7 @@
* @param device The device the permissions are for
*/
void removeDevicePermissions(@NonNull UsbDevice device) {
- synchronized (mLock) {
- mDevicePermissionMap.remove(device.getDeviceName());
- }
+ mUsbPermissionManager.removeDevicePermissions(device);
}
/**
@@ -107,9 +98,7 @@
* @param accessory The accessory the permissions are for
*/
void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
- synchronized (mLock) {
- mAccessoryPermissionMap.remove(accessory);
- }
+ mUsbPermissionManager.removeAccessoryPermissions(accessory);
}
/**
@@ -170,35 +159,17 @@
}
public boolean hasPermission(UsbDevice device, String packageName, int uid) {
- synchronized (mLock) {
- if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, uid)) {
- return false;
- }
- }
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
- if (uidList == null) {
+ if (isCameraDevicePresent(device)) {
+ if (!isCameraPermissionGranted(packageName, uid)) {
return false;
}
- return uidList.get(uid);
}
+
+ return mUsbPermissionManager.hasPermission(device, uid);
}
public boolean hasPermission(UsbAccessory accessory) {
- synchronized (mLock) {
- int uid = Binder.getCallingUid();
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- return false;
- }
- return uidList.get(uid);
- }
+ return mUsbPermissionManager.hasPermission(accessory);
}
public void checkPermission(UsbDevice device, String packageName, int uid) {
@@ -213,7 +184,11 @@
}
}
- private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+ private void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ String packageName,
+ PendingIntent pi) {
final int uid = Binder.getCallingUid();
// compare uid with packageName to foil apps pretending to be someone else
@@ -227,27 +202,15 @@
throw new IllegalArgumentException("package " + packageName + " not found");
}
- long identity = Binder.clearCallingIdentity();
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPermissionActivity");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(Intent.EXTRA_INTENT, pi);
- intent.putExtra("package", packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- try {
- mUserContext.startActivityAsUser(intent, mUser);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start UsbPermissionActivity");
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ mUsbPermissionManager.requestPermissionDialog(device,
+ accessory, canBeDefault, packageName, uid, mUserContext, pi);
}
public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
- Intent intent = new Intent();
+ Intent intent = new Intent();
// respond immediately if permission has already been granted
- if (hasPermission(device, packageName, uid)) {
+ if (hasPermission(device, packageName, uid)) {
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
@@ -270,16 +233,13 @@
}
}
- // start UsbPermissionActivity so user can choose an activity
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- requestPermissionDialog(intent, packageName, pi);
+ requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi);
}
public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
- Intent intent = new Intent();
-
// respond immediately if permission has already been granted
if (hasPermission(accessory)) {
+ Intent intent = new Intent();
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
@@ -290,31 +250,16 @@
return;
}
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- requestPermissionDialog(intent, packageName, pi);
+ requestPermissionDialog(null, accessory,
+ canBeDefault(accessory, packageName), packageName, pi);
}
public void grantDevicePermission(UsbDevice device, int uid) {
- synchronized (mLock) {
- String deviceName = device.getDeviceName();
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mDevicePermissionMap.put(deviceName, uidList);
- }
- uidList.put(uid, true);
- }
+ mUsbPermissionManager.grantDevicePermission(device, uid);
}
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
- synchronized (mLock) {
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mAccessoryPermissionMap.put(accessory, uidList);
- }
- uidList.put(uid, true);
- }
+ mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
}
/**
@@ -329,42 +274,108 @@
mUser.getIdentifier());
}
+ /**
+ * Can the app be the default for the USB device. I.e. can the app be launched by default if
+ * the device is plugged in.
+ *
+ * @param device The device the app would be default for
+ * @param packageName The package name of the app
+ *
+ * @return {@code true} if the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+ ActivityInfo[] activities = getPackageActivities(packageName);
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+ UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ if (filter.matches(device)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+ * the accessory is plugged in.
+ *
+ * @param accessory The accessory the app would be default for
+ * @param packageName The package name of the app
+ *
+ * @return {@code true} if the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+ ActivityInfo[] activities = getPackageActivities(packageName);
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+ UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+ if (filter.matches(accessory)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private ActivityInfo[] getPackageActivities(String packageName) {
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+ return packageInfo.activities;
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore
+ }
+ return null;
+ }
+
public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
long token = dump.start(idName, id);
synchronized (mLock) {
dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
- for (String deviceName : mDevicePermissionMap.keySet()) {
- long devicePermissionToken = dump.start("device_permissions",
- UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
- dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(devicePermissionToken);
- }
- for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
- long accessoryPermissionToken = dump.start("accessory_permissions",
- UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
- dump.write("accessory_description",
- UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
- accessory.getDescription());
-
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(accessoryPermissionToken);
- }
+ mUsbPermissionManager.dump(dump);
List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 7c99c49..1426383 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -48,7 +48,7 @@
private MessageDigest mDigester;
public WifiAwareAgentNetworkSpecifier() {
- // do nothing, already initialized to empty
+ initialize();
}
public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) {
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 0515e06..657e5a7 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -18,6 +18,7 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -60,6 +61,13 @@
WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR);
assertEquals(dut, rereadDut);
+
+ // Ensure that individual network specifiers are satisfied by both the original & marshaled
+ // |WifiAwareNetworkAgentSpecifier instances.
+ for (WifiAwareNetworkSpecifier ns : nsSet) {
+ assertTrue(dut.satisfiesAwareNetworkSpecifier(ns));
+ assertTrue(rereadDut.satisfiesAwareNetworkSpecifier(ns));
+ }
}
/**