diff options
146 files changed, 2768 insertions, 1535 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index e54d2f6264a6..20f0ba86c00f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3663,6 +3663,7 @@ package android.os { } public final class ConfigUpdate { + field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB"; field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS"; field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS"; field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL"; diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index 66761960e0ae..2fc580805a62 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -46,6 +46,8 @@ import java.io.PrintStream; import java.util.List; public class Media extends BaseCommand { + // This doesn't belongs to any package. Setting the package name to empty string. + private static final String PACKAGE_NAME = ""; private ISessionManager mSessionService; /** @@ -104,7 +106,7 @@ public class Media extends BaseCommand { private void sendMediaKey(KeyEvent event) { try { - mSessionService.dispatchMediaKeyEvent(event, false); + mSessionService.dispatchMediaKeyEvent(PACKAGE_NAME, false, event, false); } catch (RemoteException e) { } } @@ -264,13 +266,13 @@ public class Media extends BaseCommand { } else if ("q".equals(line) || "quit".equals(line)) { break; } else if ("play".equals(line)) { - mController.play(""); + mController.play(PACKAGE_NAME); } else if ("pause".equals(line)) { - mController.pause(""); + mController.pause(PACKAGE_NAME); } else if ("next".equals(line)) { - mController.next(""); + mController.next(PACKAGE_NAME); } else if ("previous".equals(line)) { - mController.previous(""); + mController.previous(PACKAGE_NAME); } else { System.out.println("Invalid command: " + line); } diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index daafe9c0390d..8487e6794873 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -503,7 +503,8 @@ void StatsLogProcessor::flushIfNecessaryLocked( void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, const DumpReportReason dumpReportReason) { - if (mMetricsManagers.find(key) == mMetricsManagers.end()) { + if (mMetricsManagers.find(key) == mMetricsManagers.end() || + !mMetricsManagers.find(key)->second->shouldWriteToDisk()) { return; } ProtoOutputStream proto; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index a940d58b7c4d..55dde103902d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -186,7 +186,6 @@ void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, flushIfNeededLocked(dumpTimeNs); } - flushIfNeededLocked(dumpTimeNs); if (mPastBuckets.empty()) { return; } @@ -324,6 +323,10 @@ void GaugeMetricProducer::pullLocked(const int64_t timestampNs) { triggerPuller = true; break; } + case GaugeMetric::CONDITION_CHANGE_TO_TRUE: { + triggerPuller = mCondition; + break; + } default: break; } @@ -348,7 +351,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet, flushIfNeededLocked(eventTimeNs); mCondition = conditionMet; - if (mPullTagId != -1 && mCondition) { + if (mPullTagId != -1) { pullLocked(eventTimeNs); } // else: Push mode. No need to proactively pull the gauge data. } @@ -538,7 +541,14 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs) { size_t GaugeMetricProducer::byteSizeLocked() const { size_t totalSize = 0; for (const auto& pair : mPastBuckets) { - totalSize += pair.second.size() * kBucketSize; + for (const auto& bucket : pair.second) { + totalSize += bucket.mGaugeAtoms.size() * sizeof(GaugeAtom); + for (const auto& atom : bucket.mGaugeAtoms) { + if (atom.mFields != nullptr) { + totalSize += atom.mFields->size() * sizeof(FieldValue); + } + } + } } return totalSize; } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 456da982bd9b..e143b5a582dd 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -67,6 +67,10 @@ public: return !mAllowedPkg.empty(); } + bool shouldWriteToDisk() const { + return mNoReportMetricIds.size() != mAllMetricProducers.size(); + } + void dumpStates(FILE* out, bool verbose); inline bool isInTtl(const int64_t timestampNs) const { diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index fd365602427d..9b5d72bf56dc 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -234,6 +234,7 @@ message GaugeMetric { enum SamplingType { RANDOM_ONE_SAMPLE = 1; ALL_CONDITION_CHANGES = 2; + CONDITION_CHANGE_TO_TRUE = 3; } optional SamplingType sampling_type = 9 [default = RANDOM_ONE_SAMPLE] ; diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp index 6a69100b83b0..7c0736611452 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp @@ -66,6 +66,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) { baseTimeNs, configAddedTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mStatsPullerManager.ForceClearPullerCache(); int startBucketNum = processor->mMetricsManagers.begin()->second-> mAllMetricProducers[0]->getCurrentBucketNum(); @@ -211,6 +212,7 @@ TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) { baseTimeNs, configAddedTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mStatsPullerManager.ForceClearPullerCache(); int startBucketNum = processor->mMetricsManagers.begin()->second-> mAllMetricProducers[0]->getCurrentBucketNum(); @@ -311,6 +313,7 @@ TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) { baseTimeNs, configAddedTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mStatsPullerManager.ForceClearPullerCache(); int startBucketNum = processor->mMetricsManagers.begin()->second-> mAllMetricProducers[0]->getCurrentBucketNum(); diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index 545fa015b3ec..ad0256916d8f 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -110,8 +110,8 @@ StatsdConfig MakeGaugeMetricConfig(int64_t minTime) { TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { StatsService service(nullptr); SendConfig(service, MakeConfig()); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get()); @@ -124,8 +124,8 @@ TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) { TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { StatsService service(nullptr); SendConfig(service, MakeConfig()); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. // Force the uidmap to update at timestamp 2. service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get()); @@ -142,8 +142,8 @@ TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) { TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { StatsService service(nullptr); SendConfig(service, MakeConfig()); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())}); // Force the uidmap to update at timestamp 2. @@ -165,8 +165,8 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) { TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { StatsService service(nullptr); SendConfig(service, MakeConfig()); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())}); // Force the uidmap to update at timestamp 2. @@ -190,8 +190,8 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); SendConfig(service, MakeValueMetricConfig(0)); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2); @@ -207,8 +207,8 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */)); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); @@ -230,8 +230,8 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); SendConfig(service, MakeGaugeMetricConfig(0)); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2); @@ -247,8 +247,8 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { // Partial buckets don't occur when app is first installed. service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1); SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); - const long start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are - // initialized with. + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index 98a312f7ded6..febc958b235e 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -66,6 +66,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents) { baseTimeNs, configAddedTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mStatsPullerManager.ForceClearPullerCache(); int startBucketNum = processor->mMetricsManagers.begin()->second-> mAllMetricProducers[0]->getCurrentBucketNum(); @@ -172,6 +173,7 @@ TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) { baseTimeNs, configAddedTimeNs, config, cfgKey); EXPECT_EQ(processor->mMetricsManagers.size(), 1u); EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + processor->mStatsPullerManager.ForceClearPullerCache(); int startBucketNum = processor->mMetricsManagers.begin()->second-> mAllMetricProducers[0]->getCurrentBucketNum(); diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 96c3f85ba1a2..290651cb6d08 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -4105,6 +4105,7 @@ Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mContext:J Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;)V +Ldalvik/system/BaseDexClassLoader;->addDexPath(Ljava/lang/String;Z)V Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String; Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList; Ldalvik/system/BlockGuard$Policy;->onNetwork()V @@ -4145,6 +4146,10 @@ Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List; Ldalvik/system/DexPathList;->nativeLibraryPathElements:[Ldalvik/system/DexPathList$NativeLibraryElement; Ldalvik/system/DexPathList;->splitPaths(Ljava/lang/String;Z)Ljava/util/List; Ldalvik/system/DexPathList;->systemNativeLibraryDirectories:Ljava/util/List; +Ldalvik/system/SocketTagger;->get()Ldalvik/system/SocketTagger; +Ldalvik/system/SocketTagger;->tag(Ljava/net/Socket;)V +Ldalvik/system/SocketTagger;->untag(Ljava/net/Socket;)V +Ldalvik/system/VMDebug;->allowHiddenApiReflectionFrom(Ljava/lang/Class;)V Ldalvik/system/VMDebug;->dumpReferenceTables()V Ldalvik/system/VMDebug;->isDebuggerConnected()Z Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 7032a2fe21cf..e469098e22d5 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -24,6 +24,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.input.InputManager; import android.os.RemoteException; +import android.os.UserHandle; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -111,6 +112,11 @@ public class ActivityView extends ViewGroup { * @see #startActivity(Intent) */ public abstract void onActivityViewDestroyed(ActivityView view); + /** + * Called when a task is moved to the front of the stack inside the container. + * This is a filtered version of {@link TaskStackListener} + */ + public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { } } /** @@ -155,6 +161,28 @@ public class ActivityView extends ViewGroup { /** * Launch a new activity into this container. + * <p>Activity resolved by the provided {@link Intent} must have + * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be + * launched here. Also, if activity is not owned by the owner of this container, it must allow + * embedding and the caller must have permission to embed. + * <p>Note: This class must finish initializing and + * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before + * this method can be called. + * + * @param intent Intent used to launch an activity. + * @param user The UserHandle of the user to start this activity for. + * + * + * @see StateCallback + * @see #startActivity(PendingIntent) + */ + public void startActivity(@NonNull Intent intent, UserHandle user) { + final ActivityOptions options = prepareActivityOptions(); + getContext().startActivityAsUser(intent, options.toBundle(), user); + } + + /** + * Launch a new activity into this container. * <p>Activity resolved by the provided {@link PendingIntent} must have * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be * launched here. Also, if activity is not owned by the owner of this container, it must allow @@ -303,7 +331,9 @@ public class ActivityView extends ViewGroup { final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); mVirtualDisplay = displayManager.createVirtualDisplay( DISPLAY_NAME + "@" + System.identityHashCode(this), - width, height, getBaseDisplayDensity(), mSurface, 0 /* flags */); + width, height, getBaseDisplayDensity(), mSurface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY); if (mVirtualDisplay == null) { Log.e(TAG, "Failed to initialize ActivityView"); return; @@ -317,7 +347,7 @@ public class ActivityView extends ViewGroup { e.rethrowAsRuntimeException(); } mInputForwarder = InputManager.getInstance().createInputForwarder(displayId); - mTaskStackListener = new TaskBackgroundChangeListener(); + mTaskStackListener = new TaskStackListenerImpl(); try { mActivityManager.registerTaskStackListener(mTaskStackListener); } catch (RemoteException e) { @@ -403,8 +433,11 @@ public class ActivityView extends ViewGroup { * A task change listener that detects background color change of the topmost stack on our * virtual display and updates the background of the surface view. This background will be shown * when surface view is resized, but the app hasn't drawn its content in new size yet. + * It also calls StateCallback.onTaskMovedToFront to notify interested parties that the stack + * associated with the {@link ActivityView} has had a Task moved to the front. This is useful + * when needing to also bring the host Activity to the foreground at the same time. */ - private class TaskBackgroundChangeListener extends TaskStackListener { + private class TaskStackListenerImpl extends TaskStackListener { @Override public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td) @@ -413,6 +446,31 @@ public class ActivityView extends ViewGroup { return; } + StackInfo stackInfo = getTopMostStackInfo(); + if (stackInfo == null) { + return; + } + // Found the topmost stack on target display. Now check if the topmost task's + // description changed. + if (taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { + mSurfaceView.setResizeBackgroundColor(td.getBackgroundColor()); + } + } + + @Override + public void onTaskMovedToFront(int taskId) throws RemoteException { + if (mActivityViewCallback != null) { + StackInfo stackInfo = getTopMostStackInfo(); + // if StackInfo was null or unrelated to the "move to front" then there's no use + // notifying the callback + if (stackInfo != null + && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { + mActivityViewCallback.onTaskMovedToFront(stackInfo); + } + } + } + + private StackInfo getTopMostStackInfo() throws RemoteException { // Find the topmost task on our virtual display - it will define the background // color of the surface view during resizing. final int displayId = mVirtualDisplay.getDisplay().getDisplayId(); @@ -426,14 +484,10 @@ public class ActivityView extends ViewGroup { if (stackInfo.displayId != displayId) { continue; } - // Found the topmost stack on target display. Now check if the topmost task's - // description changed. - if (taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { - mSurfaceView.setResizeBackgroundColor(td.getBackgroundColor()); - } - break; + // Found the topmost stack on target display. + return stackInfo; } + return null; } } - } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 07048f98d26d..21a3c0721371 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -118,6 +118,13 @@ public class AppOpsManager { */ public static final int MODE_FOREGROUND = 4; + /** + * Flag for {@link #startWatchingMode(String, String, int, OnOpChangedListener)}: + * Also get reports if the foreground state of an op's uid changes. This only works + * when watching a particular op, not when watching a package. + * @hide + */ + public static final int WATCH_FOREGROUND_CHANGES = 1 << 0; /** * @hide @@ -1900,6 +1907,21 @@ public class AppOpsManager { /** * Monitor for changes to the operating mode for the given op in the given app package. + * You can watch op changes only for your UID. + * + * @param op The operation to monitor, one of OPSTR_*. + * @param packageName The name of the application to monitor. + * @param flags Option flags: any combination of {@link #WATCH_FOREGROUND_CHANGES} or 0. + * @param callback Where to report changes. + * @hide + */ + public void startWatchingMode(String op, String packageName, int flags, + final OnOpChangedListener callback) { + startWatchingMode(strOpToOp(op), packageName, flags, callback); + } + + /** + * Monitor for changes to the operating mode for the given op in the given app package. * * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission * you can watch changes only for your UID. @@ -1911,6 +1933,24 @@ public class AppOpsManager { */ @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { + startWatchingMode(op, packageName, 0, callback); + } + + /** + * Monitor for changes to the operating mode for the given op in the given app package. + * + * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * you can watch changes only for your UID. + * + * @param op The operation to monitor, one of OP_*. + * @param packageName The name of the application to monitor. + * @param flags Option flags: any combination of {@link #WATCH_FOREGROUND_CHANGES} or 0. + * @param callback Where to report changes. + * @hide + */ + @RequiresPermission(value=android.Manifest.permission.WATCH_APPOPS, conditional=true) + public void startWatchingMode(int op, String packageName, int flags, + final OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); if (cb == null) { @@ -1927,7 +1967,7 @@ public class AppOpsManager { mModeWatchers.put(callback, cb); } try { - mService.startWatchingMode(op, packageName, cb); + mService.startWatchingModeWithFlags(op, packageName, flags, cb); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2080,6 +2120,19 @@ public class AppOpsManager { } /** + * Like {@link #checkOp} but returns the <em>raw</em> mode associated with the op. + * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. + * @hide + */ + public int unsafeCheckOpRaw(String op, int uid, String packageName) { + try { + return mService.checkOperation(strOpToOp(op), uid, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Make note of an application performing an operation. Note that you must pass * in both the uid and name of the application to be checked; this function will verify * that these two match, and if not, return {@link #MODE_IGNORED}. If this call @@ -2217,7 +2270,8 @@ public class AppOpsManager { */ public int checkOpNoThrow(int op, int uid, String packageName) { try { - return mService.checkOperation(op, uid, packageName); + int mode = mService.checkOperation(op, uid, packageName); + return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 4b84ed4adc30..ca3257f24207 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -736,7 +736,13 @@ public final class LoadedApk { } if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) { - ApplicationLoaders.getDefault().addNative(mClassLoader, libPaths); + // Temporarily disable logging of disk reads on the Looper thread as this is necessary + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + ApplicationLoaders.getDefault().addNative(mClassLoader, libPaths); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } } if (addedPaths != null && addedPaths.size() > 0) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 327d4fe7e363..f771cbd89e4b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -7015,7 +7015,12 @@ public class Notification implements Parcelable contentView.setViewLayoutMarginEnd(R.id.notification_messaging, bindResult.getIconMarginEnd()); contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", - mBuilder.resolveContrastColor()); + mBuilder.isColorized() ? mBuilder.getPrimaryTextColor() + : mBuilder.resolveContrastColor()); + contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor", + mBuilder.getPrimaryTextColor()); + contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor", + mBuilder.getSecondaryTextColor()); contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd", displayImagesAtEnd); contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index dec2cd42c5f9..ea0811086d52 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1420,6 +1420,9 @@ public class Intent implements Parcelable, Cloneable { * Activity Action: Start Voice Command. * <p>Input: Nothing. * <p>Output: Nothing. + * <p class="note"> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND"; @@ -5683,9 +5686,24 @@ public class Intent implements Parcelable, Cloneable { /** - * If set, resolution of this intent may take place via an instant app not - * yet on the device if there does not yet exist an app on device to - * resolve it. + * If set in an Intent passed to {@link Context#startActivity Context.startActivity()}, + * this flag will attempt to launch an instant app if no full app on the device can already + * handle the intent. + * <p> + * When attempting to resolve instant apps externally, the following {@link Intent} properties + * are supported: + * <ul> + * <li>{@link Intent#setAction(String)}</li> + * <li>{@link Intent#addCategory(String)}</li> + * <li>{@link Intent#setData(Uri)}</li> + * <li>{@link Intent#setType(String)}</li> + * <li>{@link Intent#setPackage(String)}</li> + * <li>{@link Intent#addFlags(int)}</li> + * </ul> + * <p> + * In the case that no instant app can be found, the installer will be launched to notify the + * user that the intent could not be resolved. On devices that do not support instant apps, + * the flag will be ignored. */ public static final int FLAG_ACTIVITY_MATCH_EXTERNAL = 0x00000800; diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 07fbfb50f193..52e28a4b4b9b 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -16,6 +16,10 @@ package android.content.pm; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.res.XmlResourceParser; @@ -29,7 +33,11 @@ import android.text.TextUtils; import android.util.Printer; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; import java.text.Collator; +import java.util.BitSet; import java.util.Comparator; /** @@ -42,6 +50,47 @@ import java.util.Comparator; * in the implementation of Parcelable in subclasses. */ public class PackageItemInfo { + private static final int LINE_FEED_CODE_POINT = 10; + private static final int NBSP_CODE_POINT = 160; + + /** + * Flags for {@link #loadSafeLabel(PackageManager, float, int)} + * + * @hide + */ + @Retention(SOURCE) + @IntDef(flag = true, prefix = "SAFE_LABEL_FLAG_", + value = {SAFE_LABEL_FLAG_TRIM, SAFE_LABEL_FLAG_SINGLE_LINE, + SAFE_LABEL_FLAG_FIRST_LINE}) + public @interface SafeLabelFlags {} + + /** + * Remove {@link Character#isWhitespace(int) whitespace} and non-breaking spaces from the edges + * of the label. + * + * @see #loadSafeLabel(PackageManager, float, int) + * @hide + */ + public static final int SAFE_LABEL_FLAG_TRIM = 0x1; + + /** + * Force entire string into single line of text (no newlines). Cannot be set at the same time as + * {@link #SAFE_LABEL_FLAG_FIRST_LINE}. + * + * @see #loadSafeLabel(PackageManager, float, int) + * @hide + */ + public static final int SAFE_LABEL_FLAG_SINGLE_LINE = 0x2; + + /** + * Return only first line of text (truncate at first newline). Cannot be set at the same time as + * {@link #SAFE_LABEL_FLAG_SINGLE_LINE}. + * + * @see #loadSafeLabel(PackageManager, float, int) + * @hide + */ + public static final int SAFE_LABEL_FLAG_FIRST_LINE = 0x4; + private static final float MAX_LABEL_SIZE_PX = 500f; /** The maximum length of a safe label, in characters */ private static final int MAX_SAFE_LABEL_LENGTH = 50000; @@ -164,18 +213,7 @@ public class PackageItemInfo { } /** - * Same as {@link #loadLabel(PackageManager)} with the addition that - * the returned label is safe for being presented in the UI since it - * will not contain new lines and the length will be limited to a - * reasonable amount. This prevents a malicious party to influence UI - * layout via the app label misleading the user into performing a - * detrimental for them action. If the label is too long it will be - * truncated and ellipsized at the end. - * - * @param pm A PackageManager from which the label can be loaded; usually - * the PackageManager from which you originally retrieved this item - * @return Returns a CharSequence containing the item's label. If the - * item does not have a label, its name is returned. + * Deprecated use loadSafeLabel(PackageManager, float, int) instead * * @hide */ @@ -225,6 +263,216 @@ public class PackageItemInfo { TextUtils.TruncateAt.END); } + private static boolean isNewline(int codePoint) { + int type = Character.getType(codePoint); + return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR + || codePoint == LINE_FEED_CODE_POINT; + } + + private static boolean isWhiteSpace(int codePoint) { + return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT; + } + + /** + * A special string manipulation class. Just records removals and executes the when onString() + * is called. + */ + private static class StringWithRemovedChars { + /** The original string */ + private final String mOriginal; + + /** + * One bit per char in string. If bit is set, character needs to be removed. If whole + * bit field is not initialized nothing needs to be removed. + */ + private BitSet mRemovedChars; + + StringWithRemovedChars(@NonNull String original) { + mOriginal = original; + } + + /** + * Mark all chars in a range {@code [firstRemoved - firstNonRemoved[} (not including + * firstNonRemoved) as removed. + */ + void removeRange(int firstRemoved, int firstNonRemoved) { + if (mRemovedChars == null) { + mRemovedChars = new BitSet(mOriginal.length()); + } + + mRemovedChars.set(firstRemoved, firstNonRemoved); + } + + /** + * Remove all characters before {@code firstNonRemoved}. + */ + void removeAllCharBefore(int firstNonRemoved) { + if (mRemovedChars == null) { + mRemovedChars = new BitSet(mOriginal.length()); + } + + mRemovedChars.set(0, firstNonRemoved); + } + + /** + * Remove all characters after and including {@code firstRemoved}. + */ + void removeAllCharAfter(int firstRemoved) { + if (mRemovedChars == null) { + mRemovedChars = new BitSet(mOriginal.length()); + } + + mRemovedChars.set(firstRemoved, mOriginal.length()); + } + + @Override + public String toString() { + // Common case, no chars removed + if (mRemovedChars == null) { + return mOriginal; + } + + StringBuilder sb = new StringBuilder(mOriginal.length()); + for (int i = 0; i < mOriginal.length(); i++) { + if (!mRemovedChars.get(i)) { + sb.append(mOriginal.charAt(i)); + } + } + + return sb.toString(); + } + + /** + * Return length or the original string + */ + int length() { + return mOriginal.length(); + } + + /** + * Return if a certain {@code offset} of the original string is removed + */ + boolean isRemoved(int offset) { + return mRemovedChars != null && mRemovedChars.get(offset); + } + + /** + * Return codePoint of original string at a certain {@code offset} + */ + int codePointAt(int offset) { + return mOriginal.codePointAt(offset); + } + } + + /** + * Load, clean up and truncate label before use. + * + * <p>This method is meant to remove common mistakes and nefarious formatting from strings that + * are used in sensitive parts of the UI. + * + * <p>This method first treats the string like HTML and then ... + * <ul> + * <li>Removes new lines or truncates at first new line + * <li>Trims the white-space off the end + * <li>Truncates the string to a given length + * </ul> + * ... if specified. + * + * @param ellipsizeDip Assuming maximum length of the string (in dip), assuming font size 42. + * This is roughly 50 characters for {@code ellipsizeDip == 1000}.<br /> + * Usually ellipsizing should be left to the view showing the string. If a + * string is used as an input to another string, it might be useful to + * control the length of the input string though. {@code 0} disables this + * feature. + * @return The safe label + * @hide + */ + public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm, + @FloatRange(from = 0) float ellipsizeDip, @SafeLabelFlags int flags) { + boolean onlyKeepFirstLine = ((flags & SAFE_LABEL_FLAG_FIRST_LINE) != 0); + boolean forceSingleLine = ((flags & SAFE_LABEL_FLAG_SINGLE_LINE) != 0); + boolean trim = ((flags & SAFE_LABEL_FLAG_TRIM) != 0); + + Preconditions.checkNotNull(pm); + Preconditions.checkArgument(ellipsizeDip >= 0); + Preconditions.checkFlagsArgument(flags, SAFE_LABEL_FLAG_TRIM | SAFE_LABEL_FLAG_SINGLE_LINE + | SAFE_LABEL_FLAG_FIRST_LINE); + Preconditions.checkArgument(!(onlyKeepFirstLine && forceSingleLine), + "Cannot set SAFE_LABEL_FLAG_SINGLE_LINE and SAFE_LABEL_FLAG_FIRST_LINE at the same " + + "time"); + + // loadLabel() always returns non-null + String label = loadUnsafeLabel(pm).toString(); + + // Treat string as HTML. This + // - converts HTML symbols: e.g. ß -> ß + // - applies some HTML tags: e.g. <br> -> \n + // - removes invalid characters such as \b + // - removes html styling, such as <b> + // - applies html formatting: e.g. a<p>b</p>c -> a\n\nb\n\nc + // - replaces some html tags by "object replacement" markers: <img> -> \ufffc + // - Removes leading white space + // - Removes all trailing white space beside a single space + // - Collapses double white space + StringWithRemovedChars labelStr = new StringWithRemovedChars( + Html.fromHtml(label).toString()); + + int firstNonWhiteSpace = -1; + int firstTrailingWhiteSpace = -1; + + // Remove new lines (if requested) and control characters. + int labelLength = labelStr.length(); + for (int offset = 0; offset < labelLength; ) { + int codePoint = labelStr.codePointAt(offset); + int type = Character.getType(codePoint); + int codePointLen = Character.charCount(codePoint); + boolean isNewline = isNewline(codePoint); + + if (offset > MAX_SAFE_LABEL_LENGTH || onlyKeepFirstLine && isNewline) { + labelStr.removeAllCharAfter(offset); + break; + } else if (forceSingleLine && isNewline) { + labelStr.removeRange(offset, offset + codePointLen); + } else if (type == Character.CONTROL && !isNewline) { + labelStr.removeRange(offset, offset + codePointLen); + } else if (trim && !isWhiteSpace(codePoint)) { + // This is only executed if the code point is not removed + if (firstNonWhiteSpace == -1) { + firstNonWhiteSpace = offset; + } + firstTrailingWhiteSpace = offset + codePointLen; + } + + offset += codePointLen; + } + + if (trim) { + // Remove leading and trailing white space + if (firstNonWhiteSpace == -1) { + // No non whitespace found, remove all + labelStr.removeAllCharAfter(0); + } else { + if (firstNonWhiteSpace > 0) { + labelStr.removeAllCharBefore(firstNonWhiteSpace); + } + if (firstTrailingWhiteSpace < labelLength) { + labelStr.removeAllCharAfter(firstTrailingWhiteSpace); + } + } + } + + if (ellipsizeDip == 0) { + return labelStr.toString(); + } else { + // Truncate + final TextPaint paint = new TextPaint(); + paint.setTextSize(42); + + return TextUtils.ellipsize(labelStr.toString(), paint, ellipsizeDip, + TextUtils.TruncateAt.END); + } + } + /** * Retrieve the current graphical icon associated with this item. This * will call back on the given PackageManager to load the icon from diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 1b80d3d5ee57..9154ce0b1455 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1660,23 +1660,29 @@ public class Camera { * @see ShutterCallback */ public final boolean enableShutterSound(boolean enabled) { - if (!enabled) { - IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); - IAudioService audioService = IAudioService.Stub.asInterface(b); - try { - if (audioService.isCameraSoundForced()) return false; - } catch (RemoteException e) { - Log.e(TAG, "Audio service is unavailable for queries"); + boolean canDisableShutterSound = true; + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) { + canDisableShutterSound = false; } + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } + if (!enabled && !canDisableShutterSound) { + return false; } synchronized (mShutterSoundLock) { - if (enabled && mHasAppOpsPlayAudio) { - Log.i(TAG, "Shutter sound is not allowed by AppOpsManager"); - return false; - } + mShutterSoundEnabledFromApp = enabled; + // Return the result of _enableShutterSound(enabled) in all cases. + // If the shutter sound can be disabled, disable it when the device is in DnD mode. boolean ret = _enableShutterSound(enabled); - if (ret) { - mShutterSoundEnabledFromApp = enabled; + if (enabled && !mHasAppOpsPlayAudio) { + Log.i(TAG, "Shutter sound is not allowed by AppOpsManager"); + if (canDisableShutterSound) { + _enableShutterSound(false); + } } return ret; } @@ -1739,9 +1745,18 @@ public class Camera { } if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) { if (!mHasAppOpsPlayAudio) { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) { + return; + } + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } _enableShutterSound(false); } else { - _enableShutterSound(mShutterSoundEnabledFromApp); + enableShutterSound(mShutterSoundEnabledFromApp); } } } diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java index dda0ed8a5e20..53b1c5183f1a 100644 --- a/core/java/android/os/ConfigUpdate.java +++ b/core/java/android/os/ConfigUpdate.java @@ -90,6 +90,14 @@ public final class ConfigUpdate { public static final String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST"; + /** + * Update carrier id config file. + * @hide + */ + @SystemApi + public static final String ACTION_UPDATE_CARRIER_ID_DB + = "android.os.action.UPDATE_CARRIER_ID_DB"; + private ConfigUpdate() { } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 98d9bea442f9..10ee728dfc89 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -123,6 +123,10 @@ public final class Settings { * Input: Nothing. * <p> * Output: Nothing. + * + * <p class="note"> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS"; @@ -882,6 +886,10 @@ public final class Settings { * Applications can also use {@link android.net.ConnectivityManager#getRestrictBackgroundStatus * ConnectivityManager#getRestrictBackgroundStatus()} to determine the * status of the background data restrictions for them. + * + * <p class="note"> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = @@ -1148,6 +1156,10 @@ public final class Settings { * Input: Nothing. * <p> * Output: Nothing. + * + * <p class="note"> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS @@ -8902,6 +8914,14 @@ public final class Settings { public static final String PRIV_APP_OOB_ENABLED = "priv_app_oob_enabled"; /** + * Comma separated list of privileged package names, which will be running out-of-box APK. + * Default: "ALL" + * + * @hide + */ + public static final String PRIV_APP_OOB_LIST = "priv_app_oob_list"; + + /** * The interval in milliseconds at which location requests will be throttled when they are * coming from the background. * @@ -10417,6 +10437,25 @@ public final class Settings { public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants"; /** + * App ops specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * "state_settle_time=10000" + * + * The following keys are supported: + * + * <pre> + * state_settle_time (long) + * </pre> + * + * <p> + * Type: string + * @hide + * @see com.android.server.AppOpsService.Constants + */ + public static final String APP_OPS_CONSTANTS = "app_ops_constants"; + + /** * Device Idle (Doze) specific settings. * This is encoded as a key=value list, separated by commas. Ex: * diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index 14727f027897..00060ab8ef4a 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -253,7 +253,7 @@ public final class SEService { * @return String containing the OpenMobile API version (e.g. "3.0"). */ public @NonNull String getVersion() { - return "3.2"; + return "3.3"; } @NonNull ISecureElementListener getListener() { diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java index ce94315c4225..362b94b83c3f 100644 --- a/core/java/android/speech/RecognizerIntent.java +++ b/core/java/android/speech/RecognizerIntent.java @@ -141,6 +141,10 @@ public class RecognizerIntent { * <ul> * <li>{@link #EXTRA_SECURE} * </ul> + * + * <p class="note"> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. */ public static final String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE"; diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 2505ea5d448b..0ed972477123 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -54,4 +54,6 @@ interface IAppOpsService { void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback); void stopWatchingActive(IAppOpsActiveCallback callback); boolean isOperationActive(int code, int uid, String packageName); + + void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback); } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index cbd3ad5980f5..4ee950aa2beb 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -68,6 +68,10 @@ public final class Zygote { */ public static final int API_ENFORCEMENT_POLICY_SHIFT = Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK); + /** + * Enable system server ART profiling. + */ + public static final int PROFILE_SYSTEM_SERVER = 1 << 14; /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 33049be4b8ed..da195601f725 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -702,6 +702,12 @@ public class ZygoteInit { ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); + boolean profileSystemServer = SystemProperties.getBoolean( + "dalvik.vm.profilesystemserver", false); + if (profileSystemServer) { + parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER; + } + /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java index a3c7a9ef6f91..fbf690fa1457 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -370,6 +370,14 @@ public class DividerSnapAlgorithm { return snapTarget; } + /** + * @return whether or not there are more than 1 split targets that do not include the two + * dismiss targets, used in deciding to display the middle target for accessibility + */ + public boolean showMiddleSplitTargetForAccessibility() { + return (mTargets.size() - 2) > 1; + } + public boolean isFirstSplitTargetAvailable() { return mFirstSplitTarget != mMiddleTarget; } diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java index ebc2c716493a..19593011e751 100644 --- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java +++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java @@ -23,7 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.media.AudioManager; -import android.media.session.MediaSessionLegacyHelper; +import android.media.session.MediaSessionManager; import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; @@ -48,6 +48,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { KeyguardManager mKeyguardManager; SearchManager mSearchManager; TelephonyManager mTelephonyManager; + MediaSessionManager mMediaSessionManager; public PhoneFallbackEventHandler(Context context) { mContext = context; @@ -84,8 +85,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, false); + handleVolumeKeyEvent(event); return true; } @@ -216,8 +216,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_MUTE: { if (!event.isCanceled()) { - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, false); + handleVolumeKeyEvent(event); } return true; } @@ -306,12 +305,25 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler { return mAudioManager; } + MediaSessionManager getMediaSessionManager() { + if (mMediaSessionManager == null) { + mMediaSessionManager = + (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); + } + return mMediaSessionManager; + } + void sendCloseSystemWindows() { PhoneWindow.sendCloseSystemWindows(mContext, null); } + private void handleVolumeKeyEvent(KeyEvent keyEvent) { + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent, + AudioManager.USE_DEFAULT_STREAM_TYPE); + } + private void handleMediaKeyEvent(KeyEvent keyEvent) { - MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false); + getMediaSessionManager().dispatchMediaKeyEventAsSystemService(keyEvent); } private boolean isUserSetupComplete() { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 7ea023eb50b2..3fe8f85aeaa9 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.*; import android.app.ActivityManager; import android.app.SearchManager; +import android.media.session.MediaSessionManager; import android.os.UserHandle; import android.text.TextUtils; @@ -74,7 +75,6 @@ import android.graphics.Color; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.session.MediaController; -import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -252,6 +252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private AudioManager mAudioManager; private KeyguardManager mKeyguardManager; + private MediaSessionManager mMediaSessionManager; private int mUiOptions = 0; @@ -1873,22 +1874,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If we have a session send it the volume command, otherwise // use the suggested stream. if (mMediaController != null) { - int direction = 0; - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_UP: - direction = AudioManager.ADJUST_RAISE; - break; - case KeyEvent.KEYCODE_VOLUME_DOWN: - direction = AudioManager.ADJUST_LOWER; - break; - case KeyEvent.KEYCODE_VOLUME_MUTE: - direction = AudioManager.ADJUST_TOGGLE_MUTE; - break; - } - mMediaController.adjustVolume(direction, AudioManager.FLAG_SHOW_UI); + mMediaController.dispatchVolumeButtonEventAsSystemService(event); } else { - MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent( - event, mVolumeControlStreamType, false); + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(event, + mVolumeControlStreamType); } return true; } @@ -1906,7 +1895,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { if (mMediaController != null) { - if (mMediaController.dispatchMediaButtonEvent(event)) { + if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) { return true; } } @@ -1948,6 +1937,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mAudioManager; } + private MediaSessionManager getMediaSessionManager() { + if (mMediaSessionManager == null) { + mMediaSessionManager = (MediaSessionManager) getContext().getSystemService( + Context.MEDIA_SESSION_SERVICE); + } + return mMediaSessionManager; + } + /** * A key was released and not handled by anything else in the window. * @@ -1969,12 +1966,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // If we have a session send it the volume command, otherwise // use the suggested stream. if (mMediaController != null) { - final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE - | AudioManager.FLAG_FROM_KEY; - mMediaController.adjustVolume(0, flags); + mMediaController.dispatchVolumeButtonEventAsSystemService(event); } else { - MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent( - event, mVolumeControlStreamType, false); + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService( + event, mVolumeControlStreamType); } return true; } @@ -1983,8 +1978,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // doesn't have one of these. In this case, we execute it here and // eat the event instead, because we have mVolumeControlStreamType // and they don't. - MediaSessionLegacyHelper.getHelper(getContext()).sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, false); + getMediaSessionManager().dispatchVolumeKeyEventAsSystemService( + event, AudioManager.USE_DEFAULT_STREAM_TYPE); return true; } // These are all the recognized media key codes in @@ -2001,7 +1996,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { if (mMediaController != null) { - if (mMediaController.dispatchMediaButtonEvent(event)) { + if (mMediaController.dispatchMediaButtonEventAsSystemService(event)) { return true; } } diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 0f1307873f28..318bccf68f57 100644 --- a/core/java/com/android/internal/util/NotificationColorUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -418,10 +418,23 @@ public class NotificationColorUtil { * * @param isBgDarker {@code true} if {@code bg} is darker than {@code color}. */ - private static int ensureTextContrast(int color, int bg, boolean isBgDarker) { + public static int ensureTextContrast(int color, int bg, boolean isBgDarker) { + return ensureContrast(color, bg, isBgDarker, 4.5); + } + + /** + * Finds a color with sufficient contrast over bg that has the same or darker hue as the + * original color, depending on the value of {@code isBgDarker}. + * + * @param color the color to start searching from + * @param bg the color to ensure contrast against + * @param isBgDarker {@code true} if {@code bg} is darker than {@code color} + * @param minRatio the minimum contrast ratio required + */ + public static int ensureContrast(int color, int bg, boolean isBgDarker, double minRatio) { return isBgDarker - ? findContrastColorAgainstDark(color, bg, true, 4.5) - : findContrastColor(color, bg, true, 4.5); + ? findContrastColorAgainstDark(color, bg, true, minRatio) + : findContrastColor(color, bg, true, minRatio); } /** Finds a background color for a text view with given text color and hint text color, that diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index b9a8864600c7..7116f3a92576 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -148,8 +148,6 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } mAvatarView.setVisibility(VISIBLE); mSenderName.setVisibility(TextUtils.isEmpty(nameOverride) ? GONE : VISIBLE); - mTextColor = getNormalTextColor(); - mSendingTextColor = calculateSendingTextColor(); } public void setSending(boolean sending) { @@ -160,10 +158,6 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } - private int getNormalTextColor() { - return mContext.getColor(R.color.notification_secondary_text_color_light); - } - private int calculateSendingTextColor() { TypedValue alphaValue = new TypedValue(); mContext.getResources().getValue( @@ -363,6 +357,13 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou } } + public void setTextColors(int senderTextColor, int messageTextColor) { + mTextColor = messageTextColor; + mSendingTextColor = calculateSendingTextColor(); + updateMessageColor(); + mSenderName.setTextColor(senderTextColor); + } + public void setLayoutColor(int layoutColor) { if (layoutColor != mLayoutColor){ mLayoutColor = layoutColor; diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 538ea11b6aa9..79576bd60044 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -73,6 +73,8 @@ public class MessagingLayout extends FrameLayout { private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); private TextView mTitleView; private int mLayoutColor; + private int mSenderTextColor; + private int mMessageTextColor; private int mAvatarSize; private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Paint mTextPaint = new Paint(); @@ -301,6 +303,16 @@ public class MessagingLayout extends FrameLayout { mIsOneToOne = oneToOne; } + @RemotableViewMethod + public void setSenderTextColor(int color) { + mSenderTextColor = color; + } + + @RemotableViewMethod + public void setMessageTextColor(int color) { + mMessageTextColor = color; + } + public void setUser(Person user) { mUser = user; if (mUser.getIcon() == null) { @@ -344,6 +356,7 @@ public class MessagingLayout extends FrameLayout { } newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd); newGroup.setLayoutColor(mLayoutColor); + newGroup.setTextColors(mSenderTextColor, mMessageTextColor); Person sender = senders.get(groupIndex); CharSequence nameOverride = null; if (sender != mUser && mNameReplacement != null) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f8dd7ac82255..9da3b21a613e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -747,6 +747,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) jittransitionweightOptBuf, "-Xjittransitionweight:"); + property_get("dalvik.vm.profilebootimage", propBuf, ""); + if (strcmp(propBuf, "true") == 0) { + addOption("-Xps-profile-boot-class-path"); + addOption("-Xps-profile-aot-code"); + } + /* * Madvise related options. */ diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp index 3ea604985500..df735ae12feb 100644 --- a/core/jni/android/graphics/ImageDecoder.cpp +++ b/core/jni/android/graphics/ImageDecoder.cpp @@ -139,18 +139,9 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file", nullptr, source); } - std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); - - if (::lseek(descriptor, 0, SEEK_CUR) == 0) { - return native_create(env, std::move(fileStream), source); - } - // FIXME: This allows us to pretend the current location is the beginning, - // but it would be better if SkFILEStream allowed treating its starting - // point as the beginning. - std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), - SkCodec::MinBufferedBytesNeeded())); - return native_create(env, std::move(stream), source); + std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); + return native_create(env, std::move(fileStream), source); } static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1f8d43cb8a8c..87d891539a02 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4262,7 +4262,7 @@ <receiver android:name="com.android.server.updates.CarrierIdInstallReceiver" android:permission="android.permission.UPDATE_CONFIG"> <intent-filter> - <action android:name="com.android.internal.intent.action.UPDATE_CARRIER_ID_DB" /> + <action android:name="android.os.action.UPDATE_CARRIER_ID_DB" /> <data android:scheme="content" android:host="*" android:mimeType="*/*" /> </intent-filter> </receiver> diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml index fdc9f0176dba..c8864c2f829d 100644 --- a/core/res/res/layout/notification_template_material_ambient.xml +++ b/core/res/res/layout/notification_template_material_ambient.xml @@ -59,7 +59,7 @@ android:singleLine="true" android:ellipsize="marquee" android:fadingEdge="horizontal" - android:textSize="24sp" + android:textSize="@dimen/notification_ambient_title_text_size" android:textColor="#ffffffff" /> <TextView android:id="@+id/text" @@ -70,7 +70,7 @@ android:layout_weight="1" android:gravity="top|center_horizontal" android:visibility="gone" - android:textSize="16sp" + android:textSize="@dimen/notification_ambient_text_size" android:textColor="#eeffffff" android:layout_marginTop="4dp" android:ellipsize="end" diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 095a632c4acd..79a7b903f82f 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -137,7 +137,7 @@ <color name="notification_primary_text_color_light">@color/primary_text_default_material_light</color> <color name="notification_primary_text_color_dark">@color/primary_text_default_material_dark</color> <color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color> - <item name="notification_secondary_text_disabled_alpha" format="float" type="dimen">0.30</item> + <item name="notification_secondary_text_disabled_alpha" format="float" type="dimen">0.38</item> <color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color> <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> <color name="notification_default_color_light">#a3202124</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 780cda2fb882..bc43d91d9d19 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -506,11 +506,11 @@ <!-- String containing the apn value for tethering. May be overriden by secure settings TETHER_DUN_APN. Value is a comma separated series of strings: "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type", - Or string format of ApnSettingV3. + Or string format of ApnSettingV3 or higher. note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" Multiple entries are separated by using string-array: "<item>[ApnSettingV3]Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn</item> - <item>[ApnSettingV3]Name1,apn2,,,,,,,,,123,46,,mms|*,IPV6,IP,true,12,,,,,,,,</item>" --> + <item>[ApnSettingV5]Name1,apn2,,,,,,,,,123,46,,mms|*,IPV6,IP,true,12,,,,,,,,,,</item>" --> <string-array translatable="false" name="config_tether_apndata"> </string-array> @@ -665,7 +665,7 @@ <!-- Wifi driver supports IEEE80211AC for softap --> <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool> - + <!-- Flag indicating whether the we should enable the automatic brightness in Settings. Software implementation will be used if config_hardware_auto_brightness_available is not set --> <bool name="config_automatic_brightness_available">false</bool> @@ -3423,4 +3423,8 @@ <!-- Package name for ManagedProvisioning which is responsible for provisioning work profiles. --> <string name="config_managed_provisioning_package" translatable="false">com.android.managedprovisioning</string> + + <!-- Whether or not swipe up gesture is enabled by default --> + <bool name="config_swipe_up_gesture_default">false</bool> + </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 791f7c68369f..2e8c7f9fc402 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -374,6 +374,10 @@ <dimen name="notification_title_text_size">14sp</dimen> <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) --> <dimen name="notification_subtext_size">12sp</dimen> + <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) --> + <dimen name="notification_ambient_text_size">16sp</dimen> + <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) --> + <dimen name="notification_ambient_title_text_size">24sp</dimen> <!-- Top padding for notifications in the standard layout. --> <dimen name="notification_top_pad">10dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index de9f3b024cde..db2aa8ecb16b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3301,6 +3301,8 @@ <java-symbol type="string" name="shortcut_restore_signature_mismatch" /> <java-symbol type="string" name="shortcut_restore_unknown_issue" /> + <java-symbol type="bool" name="config_swipe_up_gesture_default" /> + <!-- From media projection --> <java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" /> <java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" /> diff --git a/core/tests/coretests/src/android/os/VintfObjectTest.java b/core/tests/coretests/src/android/os/VintfObjectTest.java index 821ee806743d..44510c2379f5 100644 --- a/core/tests/coretests/src/android/os/VintfObjectTest.java +++ b/core/tests/coretests/src/android/os/VintfObjectTest.java @@ -20,6 +20,9 @@ import junit.framework.Assert; import junit.framework.TestCase; public class VintfObjectTest extends TestCase { + /** + * Sanity check for {@link VintfObject#report VintfObject.report()}. + */ public void testReport() { String[] xmls = VintfObject.report(); assertTrue(xmls.length > 0); @@ -28,6 +31,6 @@ public class VintfObjectTest extends TestCase { "<manifest version=\"1.0\" type=\"framework\">")); // From /system/compatibility-matrix.xml assertTrue(String.join("", xmls).contains( - "<compatibility-matrix version=\"1.0\" type=\"framework\">")); + "<compatibility-matrix version=\"1.0\" type=\"framework\"")); } } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index ceb58f6c8c00..18bc20c03475 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -115,6 +115,7 @@ public class SettingsBackupTest { Settings.Global.APN_DB_UPDATE_CONTENT_URL, Settings.Global.APN_DB_UPDATE_METADATA_URL, Settings.Global.APP_IDLE_CONSTANTS, + Settings.Global.APP_OPS_CONSTANTS, Settings.Global.APP_STANDBY_ENABLED, Settings.Global.ASSISTED_GPS_ENABLED, Settings.Global.AUDIO_SAFE_VOLUME_STATE, @@ -357,6 +358,7 @@ public class SettingsBackupTest { Settings.Global.POWER_MANAGER_CONSTANTS, Settings.Global.PREFERRED_NETWORK_MODE, Settings.Global.PRIV_APP_OOB_ENABLED, + Settings.Global.PRIV_APP_OOB_LIST, Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS, Settings.Global.RADIO_BLUETOOTH, Settings.Global.RADIO_CELL, diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index c486e68d361a..aa457092ca3f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -2540,19 +2540,20 @@ public class ExifInterface { if (position < 0) { return -1; } - if (mPosition != position) { - in.seek(position); - mPosition = position; - } - - int bytesRead = in.read(buffer, offset, size); - if (bytesRead < 0) { - mPosition = -1; // need to seek on next read - return -1; - } + try { + if (mPosition != position) { + in.seek(position); + mPosition = position; + } - mPosition += bytesRead; - return bytesRead; + int bytesRead = in.read(buffer, offset, size); + if (bytesRead >= 0) { + mPosition += bytesRead; + return bytesRead; + } + } catch (IOException e) {} + mPosition = -1; // need to seek on next read + return -1; } @Override diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 06f5863a6944..b4f52f9d00bc 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -37,7 +37,7 @@ import java.util.List; */ interface ISessionController { void sendCommand(String packageName, String command, in Bundle args, in ResultReceiver cb); - boolean sendMediaButton(String packageName, in KeyEvent mediaButton); + boolean sendMediaButton(String packageName, boolean asSystemService, in KeyEvent mediaButton); void registerCallbackListener(in ISessionControllerCallback cb); void unregisterCallbackListener(in ISessionControllerCallback cb); boolean isTransportControlEnabled(); @@ -46,7 +46,7 @@ interface ISessionController { PendingIntent getLaunchPendingIntent(); long getFlags(); ParcelableVolumeInfo getVolumeAttributes(); - void adjustVolume(String packageName, int direction, int flags); + void adjustVolume(String packageName, boolean asSystemService, int direction, int flags); void setVolumeTo(String packageName, int value, int flags); // These commands are for the TransportControls diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 56664a9839c8..3578c160363e 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -34,9 +34,11 @@ import android.view.KeyEvent; interface ISessionManager { ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId); List<IBinder> getSessions(in ComponentName compName, int userId); - void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock); - void dispatchVolumeKeyEvent(in KeyEvent keyEvent, int stream, boolean musicOnly); - void dispatchAdjustVolume(int suggestedStream, int delta, int flags); + void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, + boolean needWakeLock); + void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent, + int stream, boolean musicOnly); + void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, int flags); void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, int userId); void removeSessionsListener(in IActiveSessionsListener listener); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 84f85e78c1a7..8c34a31b1c8f 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -126,6 +126,27 @@ public final class MediaController { * @return true if the event was sent to the session, false otherwise. */ public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { + return dispatchMediButtonEventInternal(false, keyEvent); + } + + /** + * Dispatches the media button event as system service to the session. This only effects the + * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission + * check done by the system service. + * <p> + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param keyEvent media key event + * @return {@code true} if the event was sent to the session, {@code false} otherwise + * @hide + */ + public boolean dispatchMediaButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { + return dispatchMediButtonEventInternal(true, keyEvent); + } + + private boolean dispatchMediButtonEventInternal(boolean asSystemService, + @NonNull KeyEvent keyEvent) { if (keyEvent == null) { throw new IllegalArgumentException("KeyEvent may not be null"); } @@ -133,7 +154,8 @@ public final class MediaController { return false; } try { - return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent); + return mSessionBinder.sendMediaButton(mContext.getPackageName(), asSystemService, + keyEvent); } catch (RemoteException e) { // System is dead. =( } @@ -141,6 +163,52 @@ public final class MediaController { } /** + * Dispatches the volume button event as system service to the session. This only effects the + * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission + * check done by the system service. + * <p> + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} when the + * foreground activity didn't consume the key from the hardware devices. + * + * @param keyEvent volume key event + * @hide + */ + public void dispatchVolumeButtonEventAsSystemService(@NonNull KeyEvent keyEvent) { + switch (keyEvent.getAction()) { + case KeyEvent.ACTION_DOWN: { + int direction = 0; + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_VOLUME_UP: + direction = AudioManager.ADJUST_RAISE; + break; + case KeyEvent.KEYCODE_VOLUME_DOWN: + direction = AudioManager.ADJUST_LOWER; + break; + case KeyEvent.KEYCODE_VOLUME_MUTE: + direction = AudioManager.ADJUST_TOGGLE_MUTE; + break; + } + try { + mSessionBinder.adjustVolume(mContext.getPackageName(), true, direction, + AudioManager.FLAG_SHOW_UI); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy", e); + } + } + + case KeyEvent.ACTION_UP: { + final int flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE + | AudioManager.FLAG_FROM_KEY; + try { + mSessionBinder.adjustVolume(mContext.getPackageName(), true, 0, flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy", e); + } + } + } + } + + /** * Get the current playback state for this session. * * @return The current PlaybackState or null @@ -322,7 +390,7 @@ public final class MediaController { */ public void adjustVolume(int direction, int flags) { try { - mSessionBinder.adjustVolume(mContext.getPackageName(), direction, flags); + mSessionBinder.adjustVolume(mContext.getPackageName(), false, direction, flags); } catch (RemoteException e) { Log.wtf(TAG, "Error calling adjustVolumeBy.", e); } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 5e8b8caf3ef6..6f4f20ef780e 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -122,6 +122,15 @@ public final class MediaSession { FLAG_EXCLUSIVE_GLOBAL_PRIORITY }) public @interface SessionFlags { } + private static final String EXTRA_KEY_CALLING_PACKAGE = + "android.media.session.extra.CALLING_PACKAGE"; + private static final String EXTRA_KEY_CALLING_PID = + "android.media.session.extra.CALLING_PID"; + private static final String EXTRA_KEY_CALLING_UID = + "android.media.session.extra.CALLING_UID"; + private static final String EXTRA_KEY_ORIGINAL_BUNDLE = + "android.media.session.extra.ORIGINAL_BUNDLE"; + private final Object mLock = new Object(); private final int mMaxBitmapSize; @@ -520,11 +529,15 @@ public final class MediaSession { * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo) */ public final @NonNull RemoteUserInfo getCurrentControllerInfo() { - if (mCallback == null || mCallback.mCurrentControllerInfo == null) { + return createRemoteUserInfo(getCurrentData()); + } + + private @NonNull Bundle getCurrentData() { + if (mCallback == null || mCallback.mCurrentData == null) { throw new IllegalStateException( "This should be called inside of MediaSession.Callback methods"); } - return mCallback.mCurrentControllerInfo; + return mCallback.mCurrentData; } /** @@ -556,7 +569,7 @@ public final class MediaSession { */ public String getCallingPackage() { if (mCallback != null) { - return mCallback.mCurrentControllerInfo.getPackageName(); + return createRemoteUserInfo(mCallback.mCurrentData).getPackageName(); } return null; } @@ -659,6 +672,57 @@ public final class MediaSession { } /** + * Creates the extra bundle that includes the caller information. + * + * @return An extraBundle that contains caller information + */ + private static Bundle createExtraBundle(String packageName, int pid, int uid) { + return createExtraBundle(packageName, pid, uid, null); + } + + /** + * Creates the extra bundle that includes the caller information. + * + * @param originalBundle bundle + * @return An extraBundle that contains caller information + */ + private static Bundle createExtraBundle(String packageName, int pid, int uid, + Bundle originalBundle) { + Bundle bundle = new Bundle(); + bundle.putString(EXTRA_KEY_CALLING_PACKAGE, packageName); + bundle.putInt(EXTRA_KEY_CALLING_PID, pid); + bundle.putInt(EXTRA_KEY_CALLING_UID, uid); + if (originalBundle != null) { + bundle.putBundle(EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle); + } + return bundle; + } + + /** + * Creates the {@link RemoteUserInfo} from the extra bundle created by + * {@link #createExtraBundle}. + * + * @param extraBundle that previously created by createExtraBundle() + * @return a RemoteUserInfo + */ + private static RemoteUserInfo createRemoteUserInfo(Bundle extraBundle) { + return new RemoteUserInfo( + extraBundle.getString(EXTRA_KEY_CALLING_PACKAGE), + extraBundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID), + extraBundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID)); + } + + /** + * Gets the original bundle from the extra bundle created by {@link #createExtraBundle}. + * + * @param extraBundle that previously created by createExtraBundle() + * @return a Bundle + */ + private static Bundle getOriginalBundle(Bundle extraBundle) { + return extraBundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE); + } + + /** * Return true if this is considered an active playback state. * * @hide @@ -755,9 +819,6 @@ public final class MediaSession { private MediaSession mSession; private CallbackMessageHandler mHandler; private boolean mMediaPlayPauseKeyPending; - private String mCallingPackage; - private int mCallingPid; - private int mCallingUid; public Callback() { } @@ -811,8 +872,9 @@ public final class MediaSession { } } else { mMediaPlayPauseKeyPending = true; - mHandler.sendEmptyMessageDelayed(CallbackMessageHandler - .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, + mHandler.postDelayed(CallbackMessageHandler + .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT, + mSession.getCurrentData(), ViewConfiguration.getDoubleTapTimeout()); } return true; @@ -1242,22 +1304,6 @@ public final class MediaSession { session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid)); } } - - private Bundle createExtraBundle(String packageName, int pid, int uid) { - return createExtraBundle(packageName, pid, uid, null); - } - - private Bundle createExtraBundle(String packageName, int pid, int uid, - Bundle originalBundle) { - Bundle bundle = new Bundle(); - bundle.putString(CallbackMessageHandler.EXTRA_KEY_CALLING_PACKAGE, packageName); - bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_PID, pid); - bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_UID, uid); - if (originalBundle != null) { - bundle.putBundle(CallbackMessageHandler.EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle); - } - return bundle; - } } /** @@ -1379,15 +1425,6 @@ public final class MediaSession { private class CallbackMessageHandler extends Handler { - private static final String EXTRA_KEY_CALLING_PACKAGE = - "android.media.session.extra.CALLING_PACKAGE"; - private static final String EXTRA_KEY_CALLING_PID = - "android.media.session.extra.CALLING_PID"; - private static final String EXTRA_KEY_CALLING_UID = - "android.media.session.extra.CALLING_UID"; - private static final String EXTRA_KEY_ORIGINAL_BUNDLE = - "android.media.session.extra.ORIGINAL_BUNDLE"; - private static final int MSG_COMMAND = 1; private static final int MSG_MEDIA_BUTTON = 2; private static final int MSG_PREPARE = 3; @@ -1413,8 +1450,7 @@ public final class MediaSession { private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23; private MediaSession.Callback mCallback; - - private RemoteUserInfo mCurrentControllerInfo; + private Bundle mCurrentData; public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) { super(looper, null, true); @@ -1422,22 +1458,25 @@ public final class MediaSession { mCallback.mHandler = this; } - public void post(int what, Object obj, Bundle bundle) { + public void post(int what, Object obj, Bundle data) { Message msg = obtainMessage(what, obj); - msg.setData(bundle); + msg.setData(data); msg.sendToTarget(); } + public void postDelayed(int what, Bundle data, long delayMs) { + Message msg = obtainMessage(what); + msg.setData(data); + sendMessageDelayed(msg, delayMs); + } + @Override public void handleMessage(Message msg) { VolumeProvider vp; - Bundle bundle = msg.getData(); - Bundle originalBundle = bundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE); + Bundle data = msg.getData(); + Bundle originalBundle = getOriginalBundle(data); - mCurrentControllerInfo = new RemoteUserInfo( - bundle.getString(EXTRA_KEY_CALLING_PACKAGE), - bundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID), - bundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID)); + mCurrentData = data; switch (msg.what) { case MSG_COMMAND: @@ -1521,7 +1560,7 @@ public final class MediaSession { mCallback.handleMediaPlayPauseKeySingleTapIfPending(); break; } - mCurrentControllerInfo = null; + mCurrentData = null; } } } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index f35810322cd5..f54bfc137703 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -300,8 +300,28 @@ public final class MediaSessionManager { * @hide */ public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) { + dispatchMediaKeyEventInternal(false, keyEvent, needWakeLock); + } + + /** + * Send a media key event as system component. The receiver will be selected automatically. + * <p> + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or + * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key + * from the hardware devices. + * + * @param keyEvent The KeyEvent to send. + * @hide + */ + public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) { + dispatchMediaKeyEventInternal(true, keyEvent, false); + } + + private void dispatchMediaKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent, + boolean needWakeLock) { try { - mService.dispatchMediaKeyEvent(keyEvent, needWakeLock); + mService.dispatchMediaKeyEvent(mContext.getPackageName(), asSystemService, keyEvent, + needWakeLock); } catch (RemoteException e) { Log.e(TAG, "Failed to send key event.", e); } @@ -311,12 +331,33 @@ public final class MediaSessionManager { * Send a volume key event. The receiver will be selected automatically. * * @param keyEvent The volume KeyEvent to send. - * @param needWakeLock True if a wake lock should be held while sending the key. * @hide */ public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int stream, boolean musicOnly) { + dispatchVolumeKeyEventInternal(false, keyEvent, stream, musicOnly); + } + + /** + * Dispatches the volume button event as system service to the session. This only effects the + * {@link MediaSession.Callback#getCurrentControllerInfo()} and doesn't bypass any permission + * check done by the system service. + * <p> + * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or + * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key + * from the hardware devices. + * + * @param keyEvent The KeyEvent to send. + * @hide + */ + public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) { + dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false); + } + + private void dispatchVolumeKeyEventInternal(boolean asSystemService, @NonNull KeyEvent keyEvent, + int stream, boolean musicOnly) { try { - mService.dispatchVolumeKeyEvent(keyEvent, stream, musicOnly); + mService.dispatchVolumeKeyEvent(mContext.getPackageName(), asSystemService, keyEvent, + stream, musicOnly); } catch (RemoteException e) { Log.e(TAG, "Failed to send volume key event.", e); } @@ -336,7 +377,8 @@ public final class MediaSessionManager { */ public void dispatchAdjustVolume(int suggestedStream, int direction, int flags) { try { - mService.dispatchAdjustVolume(suggestedStream, direction, flags); + mService.dispatchAdjustVolume(mContext.getPackageName(), suggestedStream, direction, + flags); } catch (RemoteException e) { Log.e(TAG, "Failed to send adjust volume.", e); } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java index 54a1af4da1ca..6a2a04a76ed5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java @@ -47,8 +47,10 @@ public final class CategoryKey { "com.android.settings.category.ia.development"; public static final String CATEGORY_NOTIFICATIONS = "com.android.settings.category.ia.notifications"; - public static final String CATEGORY_DO_NOT_DISTURB = - "com.android.settings.category.ia.dnd"; + public static final String CATEGORY_DO_NOT_DISTURB = "com.android.settings.category.ia.dnd"; + public static final String CATEGORY_GESTURES = "com.android.settings.category.ia.gestures"; + public static final String CATEGORY_NIGHT_LIGHT = + "com.android.settings.category.ia.night_light"; public static final Map<String, String> KEY_COMPAT_MAP; diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java index 06e2ee103600..b7699f180281 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java @@ -67,6 +67,19 @@ public class PowerWhitelistBackend { return mWhitelistedApps.contains(pkg); } + public boolean isWhitelisted(String[] pkgs) { + if (ArrayUtils.isEmpty(pkgs)) { + return false; + } + for (String pkg : pkgs) { + if (isWhitelisted(pkg)) { + return true; + } + } + + return false; + } + public boolean isSysWhitelistedExceptIdle(String pkg) { return mSysWhitelistedAppsExceptIdle.contains(pkg); } diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java index 6025d68a6d0e..13364aba00fd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java @@ -26,6 +26,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.VisibleForTesting; +import android.text.format.DateUtils; import android.util.IconDrawableFactory; import android.util.Log; import java.util.ArrayList; @@ -41,7 +42,8 @@ public class RecentLocationApps { @VisibleForTesting static final String ANDROID_SYSTEM_PACKAGE_NAME = "android"; - private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000; + // Keep last 24 hours of location app information. + private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; @VisibleForTesting static final int[] LOCATION_OPS = new int[] { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java index d12473e23ef9..f34605caf045 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java @@ -57,8 +57,10 @@ public class CategoryKeyTest { allKeys.add(CategoryKey.CATEGORY_SYSTEM); allKeys.add(CategoryKey.CATEGORY_SYSTEM_LANGUAGE); allKeys.add(CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT); + allKeys.add(CategoryKey.CATEGORY_GESTURES); + allKeys.add(CategoryKey.CATEGORY_NIGHT_LIGHT); // DO NOT REMOVE ANYTHING ABOVE - assertThat(allKeys.size()).isEqualTo(16); + assertThat(allKeys.size()).isEqualTo(18); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java index f591781db5d7..0af2c05a6f10 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java @@ -61,24 +61,32 @@ public class PowerWhitelistBackendTest { assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerWhitelistBackend.isWhitelisted(new String[]{PACKAGE_ONE})).isTrue(); + assertThat(mPowerWhitelistBackend.isWhitelisted(new String[]{PACKAGE_TWO})).isFalse(); mPowerWhitelistBackend.addApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue(); + assertThat(mPowerWhitelistBackend.isWhitelisted( + new String[]{PACKAGE_ONE, PACKAGE_TWO})).isTrue(); mPowerWhitelistBackend.removeApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerWhitelistBackend.isWhitelisted(new String[]{PACKAGE_ONE})).isTrue(); + assertThat(mPowerWhitelistBackend.isWhitelisted(new String[]{PACKAGE_TWO})).isFalse(); mPowerWhitelistBackend.removeApp(PACKAGE_ONE); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerWhitelistBackend.isWhitelisted( + new String[]{PACKAGE_ONE, PACKAGE_TWO})).isFalse(); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java index 5e0fcefc5209..8a54aeec6d79 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java @@ -37,8 +37,8 @@ public class RecentLocationAppsTest { // App running duration in milliseconds private static final int DURATION = 10; private static final long ONE_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(1); - private static final long FOURTEEN_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(14); - private static final long TWENTY_MIN_AGO = NOW - TimeUnit.MINUTES.toMillis(20); + private static final long TWENTY_THREE_HOURS_AGO = NOW - TimeUnit.HOURS.toMillis(23); + private static final long TWO_DAYS_AGO = NOW - TimeUnit.DAYS.toMillis(2); private static final String[] TEST_PACKAGE_NAMES = {"package_1MinAgo", "package_14MinAgo", "package_20MinAgo"}; @@ -74,7 +74,7 @@ public class RecentLocationAppsTest { when(mUserManager.getUserProfiles()) .thenReturn(Collections.singletonList(new UserHandle(mTestUserId))); - long[] testRequestTime = {ONE_MIN_AGO, FOURTEEN_MIN_AGO, TWENTY_MIN_AGO}; + long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO}; List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime); when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps); mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES); @@ -91,7 +91,7 @@ public class RecentLocationAppsTest { assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]); assertThat(requests.get(0).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION); assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]); - assertThat(requests.get(1).requestFinishTime).isEqualTo(FOURTEEN_MIN_AGO + DURATION); + assertThat(requests.get(1).requestFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO + DURATION); } @Test @@ -105,7 +105,7 @@ public class RecentLocationAppsTest { ONE_MIN_AGO, DURATION); long[] testRequestTime = - {ONE_MIN_AGO, FOURTEEN_MIN_AGO, TWENTY_MIN_AGO, ONE_MIN_AGO}; + {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO}; List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime); appOps.add(androidSystemPackageOps); when(mAppOpsManager.getPackagesForOps(RecentLocationApps.LOCATION_OPS)).thenReturn(appOps); @@ -119,7 +119,7 @@ public class RecentLocationAppsTest { assertThat(requests.get(0).packageName).isEqualTo(TEST_PACKAGE_NAMES[0]); assertThat(requests.get(0).requestFinishTime).isEqualTo(ONE_MIN_AGO + DURATION); assertThat(requests.get(1).packageName).isEqualTo(TEST_PACKAGE_NAMES[1]); - assertThat(requests.get(1).requestFinishTime).isEqualTo(FOURTEEN_MIN_AGO + DURATION); + assertThat(requests.get(1).requestFinishTime).isEqualTo(TWENTY_THREE_HOURS_AGO + DURATION); } private void mockTestApplicationInfos(int userId, String... packageNameList) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 4c98bb8cc2e8..1c635c41172a 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -254,6 +254,7 @@ public class SettingsHelper { case Settings.Secure.TOUCH_EXPLORATION_ENABLED: case Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED: case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED: + case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED: case Settings.Secure.UI_NIGHT_MODE: return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0; case Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES: diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk index bd5b1f2c64ef..1ca6afee6daf 100644 --- a/packages/SettingsProvider/test/Android.mk +++ b/packages/SettingsProvider/test/Android.mk @@ -10,7 +10,9 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) \ ../src/com/android/providers/settings/SettingsState.java \ ../src/com/android/providers/settings/SettingsHelper.java -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-test \ + truth-prebuilt LOCAL_JAVA_LIBRARIES := android.test.base diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java new file mode 100644 index 000000000000..b438e9130a88 --- /dev/null +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperRestoreTest.java @@ -0,0 +1,105 @@ +/* + * 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.providers.settings; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link SettingsHelper#restoreValue(Context, ContentResolver, ContentValues, Uri, + * String, String, int)}. Specifically verifies that we restore critical accessibility settings only + * if the user has not already configured these in SUW. + */ +@RunWith(AndroidJUnit4.class) +public class SettingsHelperRestoreTest { + private Context mContext; + private ContentResolver mContentResolver; + private SettingsHelper mSettingsHelper; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getContext(); + mContentResolver = mContext.getContentResolver(); + mSettingsHelper = new SettingsHelper(mContext); + } + + /** Tests for {@link Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED}. */ + @Test + public void + restoreAccessibilityDisplayMagnificationNavbarEnabled_alreadyConfigured_doesNotRestore() + throws Exception { + // Simulate already configuring setting via SUW. + Settings.Secure.putInt( + mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + 1); + + mSettingsHelper.restoreValue( + mContext, + mContentResolver, + new ContentValues(2), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + String.valueOf(0), + Build.VERSION.SDK_INT); + + assertThat( + Settings.Secure.getInt( + mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED)) + .isEqualTo(1); + } + + @Test + public void + restoreAccessibilityDisplayMagnificationNavbarEnabled_notAlreadyConfigured_restores() + throws Exception { + // Simulate system default at boot. + Settings.Secure.putInt( + mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + 0); + + mSettingsHelper.restoreValue( + mContext, + mContentResolver, + new ContentValues(2), + Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + String.valueOf(1), + Build.VERSION.SDK_INT); + + assertThat( + Settings.Secure.getInt( + mContentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED)) + .isEqualTo(1); + } +} diff --git a/packages/SystemUI/res/drawable/smart_reply_button_background.xml b/packages/SystemUI/res/drawable/smart_reply_button_background.xml index 93adaa00e73c..31119a90a788 100644 --- a/packages/SystemUI/res/drawable/smart_reply_button_background.xml +++ b/packages/SystemUI/res/drawable/smart_reply_button_background.xml @@ -26,7 +26,8 @@ android:insetBottom="8dp"> <shape android:shape="rectangle"> <corners android:radius="8dp" /> - <stroke android:width="1dp" android:color="@color/smart_reply_button_stroke" /> + <stroke android:width="@dimen/smart_reply_button_stroke_width" + android:color="@color/smart_reply_button_stroke" /> <solid android:color="@color/smart_reply_button_background"/> </shape> </inset> diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml index bccf2077567a..23e8a1548300 100644 --- a/packages/SystemUI/res/layout/hybrid_notification.xml +++ b/packages/SystemUI/res/layout/hybrid_notification.xml @@ -25,7 +25,6 @@ android:id="@+id/notification_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@*android:style/TextAppearance.Material.Notification.Title" android:singleLine="true" style="?attr/hybridNotificationTitleStyle" /> diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml index b5d48b4636a8..e902c925c2a9 100644 --- a/packages/SystemUI/res/layout/remote_input.xml +++ b/packages/SystemUI/res/layout/remote_input.xml @@ -42,7 +42,7 @@ android:singleLine="true" android:ellipsize="start" android:inputType="textShortMessage|textAutoCorrect|textCapSentences" - android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" /> + android:imeOptions="actionSend" /> <FrameLayout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml index a490c4b8ba60..9faed1828718 100644 --- a/packages/SystemUI/res/layout/smart_reply_button.xml +++ b/packages/SystemUI/res/layout/smart_reply_button.xml @@ -19,6 +19,7 @@ <!-- android:paddingHorizontal is set dynamically in SmartReplyView. --> <Button xmlns:android="http://schemas.android.com/apk/res/android" style="@android:style/Widget.Material.Button" + android:stateListAnimator="@null" android:layout_width="wrap_content" android:layout_height="match_parent" android:minWidth="0dp" diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml index aa5549f35a34..9fffc72bcf6d 100644 --- a/packages/SystemUI/res/layout/smart_reply_view.xml +++ b/packages/SystemUI/res/layout/smart_reply_view.xml @@ -25,6 +25,7 @@ android:layout_width="wrap_content" systemui:spacing="@dimen/smart_reply_button_spacing" systemui:singleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_single_line" - systemui:doubleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_double_line"> + systemui:doubleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_double_line" + systemui:buttonStrokeWidth="@dimen/smart_reply_button_stroke_width"> <!-- smart_reply_button(s) will be added here. --> </com.android.systemui.statusbar.policy.SmartReplyView> diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml index c8a554467ceb..7931dfe74587 100644 --- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -21,62 +21,70 @@ xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/mobile_combo" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:paddingStart="2dp" - android:orientation="horizontal"> - <FrameLayout - android:id="@+id/inout_container" - android:layout_height="17dp" + android:layout_height="match_parent" + android:gravity="center_vertical" > + + <com.android.keyguard.AlphaOptimizedLinearLayout + android:id="@+id/mobile_group" android:layout_width="wrap_content" - android:layout_gravity="center_vertical"> - <ImageView - android:id="@+id/mobile_in" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:paddingStart="2dp" + android:orientation="horizontal" > + + <FrameLayout + android:id="@+id/inout_container" + android:layout_height="17dp" android:layout_width="wrap_content" - android:src="@drawable/ic_activity_down" - android:visibility="gone" - android:paddingEnd="2dp" - /> + android:layout_gravity="center_vertical"> + <ImageView + android:id="@+id/mobile_in" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_down" + android:visibility="gone" + android:paddingEnd="2dp" + /> + <ImageView + android:id="@+id/mobile_out" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_up" + android:paddingEnd="2dp" + android:visibility="gone" + /> + </FrameLayout> <ImageView - android:id="@+id/mobile_out" + android:id="@+id/mobile_type" android:layout_height="wrap_content" android:layout_width="wrap_content" - android:src="@drawable/ic_activity_up" - android:paddingEnd="2dp" + android:layout_gravity="center_vertical" + android:paddingEnd="1dp" + android:visibility="gone" /> + <Space + android:id="@+id/mobile_roaming_space" + android:layout_height="match_parent" + android:layout_width="@dimen/roaming_icon_start_padding" android:visibility="gone" /> - </FrameLayout> - <ImageView - android:id="@+id/mobile_type" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_gravity="center_vertical" - android:paddingEnd="1dp" - android:visibility="gone" /> - <Space - android:id="@+id/mobile_roaming_space" - android:layout_height="match_parent" - android:layout_width="@dimen/roaming_icon_start_padding" - android:visibility="gone" - /> - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical"> - <com.android.systemui.statusbar.AnimatedImageView - android:id="@+id/mobile_signal" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - systemui:hasOverlappingRendering="false" - /> - <ImageView - android:id="@+id/mobile_roaming" + <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/stat_sys_roaming" - android:contentDescription="@string/data_connection_roaming" - android:visibility="gone" /> - </FrameLayout> + android:layout_gravity="center_vertical"> + <com.android.systemui.statusbar.AnimatedImageView + android:id="@+id/mobile_signal" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + systemui:hasOverlappingRendering="false" + /> + <ImageView + android:id="@+id/mobile_roaming" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> + </FrameLayout> + </com.android.keyguard.AlphaOptimizedLinearLayout> </com.android.systemui.statusbar.StatusBarMobileView> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index b11266a26abc..2ce9bfc8ae7c 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -135,6 +135,7 @@ <attr name="spacing" format="dimension" /> <attr name="singleLineButtonPaddingHorizontal" format="dimension" /> <attr name="doubleLineButtonPaddingHorizontal" format="dimension" /> + <attr name="buttonStrokeWidth" format="dimension" /> </declare-styleable> <!-- Used to style rotate suggestion button AVD animations --> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index efcca63c6d08..3472477c4ce5 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -151,7 +151,8 @@ <color name="zen_introduction">#ffffffff</color> <color name="smart_reply_button_text">#5F6368</color> - <color name="smart_reply_button_background">#feffffff</color> + <color name="smart_reply_button_text_dark_bg">@*android:color/notification_primary_text_color_dark</color> + <color name="smart_reply_button_background">#ffffffff</color> <color name="smart_reply_button_stroke">#ffdadce0</color> <!-- Fingerprint dialog colors --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f3fe29794bac..8c3cc4223fd8 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -951,6 +951,7 @@ <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen> <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen> <dimen name="smart_reply_button_min_height">48dp</dimen> + <dimen name="smart_reply_button_stroke_width">1dp</dimen> <dimen name="smart_reply_button_font_size">14sp</dimen> <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 95ccc3c29ef4..654f4079aef3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -623,6 +623,8 @@ <!-- The overflow indicator shown when a group has more notification inside the group than the visible ones. An example is "+ 3" [CHAR LIMIT=5] --> <string name="notification_group_overflow_indicator">+ <xliff:g id="number" example="3">%s</xliff:g></string> + <!-- The overflow indicator shown when a group has more notification inside the group than the visible ones. An example is "New message, +3" [CHAR LIMIT=7] --> + <string name="notification_group_overflow_indicator_ambient"><xliff:g id="notification_title" example="New message">%s</xliff:g>, +<xliff:g id="overflow" example="+3">%s</xliff:g></string> <!-- Content description describing how many more notifications are in a group [CHAR LIMIT=NONE] --> <plurals name="notification_group_overflow_description"> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index c9b14dc23877..b3f4534853c4 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -110,7 +110,6 @@ <item name="android:paddingStart">@*android:dimen/notification_extra_margin_ambient</item> <item name="android:paddingEnd">@*android:dimen/notification_extra_margin_ambient</item> <item name="android:orientation">vertical</item> - <item name="android:paddingBottom">23.5dp</item> </style> <style name="hybrid_notification"> @@ -119,22 +118,28 @@ </style> <style name="hybrid_notification_title_ambient"> + <item name="android:layout_marginTop">@*android:dimen/notification_header_margin_top_ambient</item> <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item> <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item> - <item name="android:textSize">20sp</item> + <item name="android:textAppearance">@*android:style/Notification.Header.Ambient</item> + <item name="android:layout_gravity">top|center_horizontal</item> + <item name="android:textSize">@*android:dimen/notification_ambient_title_text_size</item> <item name="android:textColor">#ffffffff</item> </style> <style name="hybrid_notification_title"> <item name="android:paddingEnd">4dp</item> + <item name="android:textAppearance">@*android:style/TextAppearance.Material.Notification.Title</item> </style> <style name="hybrid_notification_text_ambient"> <item name="android:paddingStart">@*android:dimen/notification_content_margin_start</item> <item name="android:paddingEnd">@*android:dimen/notification_content_margin_end</item> - <item name="android:textSize">16sp</item> + <item name="android:textSize">@*android:dimen/notification_ambient_text_size</item> <item name="android:textColor">#eeffffff</item> - <item name="android:layout_marginTop">4dp</item> + <item name="android:gravity">top|center_horizontal</item> + <item name="android:ellipsize">end</item> + <item name="android:maxLines">3</item> </style> <style name="hybrid_notification_text" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java index 5a0dddc7656a..cc536a50bfc0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java @@ -57,6 +57,7 @@ public class NavigationBarCompat { public static final int HIT_TARGET_BACK = 1; public static final int HIT_TARGET_HOME = 2; public static final int HIT_TARGET_OVERVIEW = 3; + public static final int HIT_TARGET_ROTATION = 4; @Retention(RetentionPolicy.SOURCE) @IntDef({FLAG_DISABLE_SWIPE_UP, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index 5b4d65290146..aa0bcc5cf2b8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -241,6 +241,10 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { mViewMediatorCallback.resetKeyguard(); } + public void resetSecurityContainer() { + mSecurityContainer.reset(); + } + @Override public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { if (mViewMediatorCallback != null) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java index a265a5e1d5a7..0ca0a117f6e6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java @@ -74,6 +74,7 @@ public class FalsingManager implements SensorEventListener { private boolean mEnforceBouncer = false; private boolean mBouncerOn = false; + private boolean mBouncerOffOnDown = false; private boolean mSessionActive = false; private boolean mIsTouchScreen = true; private int mState = StatusBarState.SHADE; @@ -459,10 +460,19 @@ public class FalsingManager implements SensorEventListener { public void onTouchEvent(MotionEvent event, int width, int height) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); - } - if (mSessionActive && !mBouncerOn) { - mDataCollector.onTouchEvent(event, width, height); - mHumanInteractionClassifier.onTouchEvent(event); + // If the bouncer was not shown during the down event, + // we want the entire gesture going to HumanInteractionClassifier + mBouncerOffOnDown = !mBouncerOn; + } + if (mSessionActive) { + if (!mBouncerOn) { + // In case bouncer is "visible", but onFullyShown has not yet been called, + // avoid adding the event to DataCollector + mDataCollector.onTouchEvent(event, width, height); + } + if (mBouncerOffOnDown) { + mHumanInteractionClassifier.onTouchEvent(event); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index ea3f95e61a35..4b6528882ca4 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -26,6 +26,7 @@ import android.app.Dialog; import android.app.KeyguardManager; import android.app.WallpaperManager; import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -699,6 +700,9 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, UserHandle.USER_ALL); try { WindowManagerGlobal.getWindowManagerService().lockNow(null); + // Lock profiles (if any) on the background thread. + final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); + bgHandler.post(() -> lockProfiles()); } catch (RemoteException e) { Log.e(TAG, "Error while trying to lock device.", e); } @@ -716,6 +720,18 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, }; } + private void lockProfiles() { + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final TrustManager tm = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); + final int currentUserId = getCurrentUser().id; + final int[] profileIds = um.getEnabledProfileIds(currentUserId); + for (final int id : profileIds) { + if (id != currentUserId) { + tm.setDeviceLockedForUser(id, true); + } + } + } + private UserInfo getCurrentUser() { try { return ActivityManager.getService().getCurrentUser(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index f60e207f9a8c..d16e1b1429b4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -69,7 +69,8 @@ public class RecentsOnboarding { private static final boolean RESET_PREFS_FOR_DEBUG = false; private static final boolean ONBOARDING_ENABLED = true; private static final long SHOW_DELAY_MS = 500; - private static final long SHOW_HIDE_DURATION_MS = 300; + private static final long SHOW_DURATION_MS = 300; + private static final long HIDE_DURATION_MS = 100; // Show swipe-up tips after opening overview from home this number of times. private static final int SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT = 3; // Show quick scrub tips after opening overview this number of times. @@ -172,6 +173,11 @@ public class RecentsOnboarding { } @Override + public void onQuickStepStarted() { + hide(true); + } + + @Override public void onQuickScrubStarted() { boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding(); if (!alreadySeenQuickScrubsOnboarding) { @@ -292,7 +298,7 @@ public class RecentsOnboarding { mHasDismissedQuickScrubTip = false; mNumAppsLaunchedSinceSwipeUpTipDismiss = 0; mOverviewOpenedCountSinceQuickScrubTipDismiss = 0; - hide(false); + hide(true); } public void onConfigurationChanged(Configuration newConfiguration) { @@ -305,15 +311,12 @@ public class RecentsOnboarding { if (!shouldShow()) { return; } - if (mLayoutAttachedToWindow) { - hide(false); - } mDismissView.setTag(stringRes); mLayout.setTag(stringRes); mTextView.setText(stringRes); // Only show in portrait. int orientation = mContext.getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_PORTRAIT) { + if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) { mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); mWindowManager.addView(mLayout, getWindowLayoutParams()); @@ -322,7 +325,7 @@ public class RecentsOnboarding { .alpha(1f) .withLayer() .setStartDelay(SHOW_DELAY_MS) - .setDuration(SHOW_HIDE_DURATION_MS) + .setDuration(SHOW_DURATION_MS) .setInterpolator(new DecelerateInterpolator()) .start(); } @@ -344,7 +347,8 @@ public class RecentsOnboarding { mLayout.animate() .alpha(0f) .withLayer() - .setDuration(SHOW_HIDE_DURATION_MS) + .setStartDelay(0) + .setDuration(HIDE_DURATION_MS) .setInterpolator(new AccelerateInterpolator()) .withEndAction(() -> mWindowManager.removeViewImmediate(mLayout)) .start(); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index c8ee87357127..6d677ab74bee 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -180,16 +180,20 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); + final DividerSnapAlgorithm snapAlgorithm = getSnapAlgorithm(); if (isHorizontalDivision()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_full, mContext.getString(R.string.accessibility_action_divider_top_full))); - if (mSnapAlgorithm.isFirstSplitTargetAvailable()) { + if (snapAlgorithm.isFirstSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_70, mContext.getString(R.string.accessibility_action_divider_top_70))); } - info.addAction(new AccessibilityAction(R.id.action_move_tl_50, + if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { + // Only show the middle target if there are more than 1 split target + info.addAction(new AccessibilityAction(R.id.action_move_tl_50, mContext.getString(R.string.accessibility_action_divider_top_50))); - if (mSnapAlgorithm.isLastSplitTargetAvailable()) { + } + if (snapAlgorithm.isLastSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_30, mContext.getString(R.string.accessibility_action_divider_top_30))); } @@ -198,13 +202,16 @@ public class DividerView extends FrameLayout implements OnTouchListener, } else { info.addAction(new AccessibilityAction(R.id.action_move_tl_full, mContext.getString(R.string.accessibility_action_divider_left_full))); - if (mSnapAlgorithm.isFirstSplitTargetAvailable()) { + if (snapAlgorithm.isFirstSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_70, mContext.getString(R.string.accessibility_action_divider_left_70))); } - info.addAction(new AccessibilityAction(R.id.action_move_tl_50, + if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { + // Only show the middle target if there are more than 1 split target + info.addAction(new AccessibilityAction(R.id.action_move_tl_50, mContext.getString(R.string.accessibility_action_divider_left_50))); - if (mSnapAlgorithm.isLastSplitTargetAvailable()) { + } + if (snapAlgorithm.isLastSplitTargetAvailable()) { info.addAction(new AccessibilityAction(R.id.action_move_tl_30, mContext.getString(R.string.accessibility_action_divider_left_30))); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 8b6b5fe5d542..364ed80638de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -581,7 +581,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } } - private void setBackgroundTintColor(int color) { + protected void setBackgroundTintColor(int color) { if (color != mCurrentBackgroundTint) { mCurrentBackgroundTint = color; if (color == mNormalColor) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 8adf4bc3d5c2..991b47e14028 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -469,6 +469,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateNotificationColor(); if (mMenuRow != null) { mMenuRow.onNotificationUpdated(mStatusBarNotification); + mMenuRow.setAppName(mAppName); } if (mIsSummaryWithChildren) { mChildrenContainer.recreateNotificationHeader(mExpandClickListener); @@ -1089,6 +1090,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + @Override + protected void setBackgroundTintColor(int color) { + super.setBackgroundTintColor(color); + NotificationContentView view = getShowingLayout(); + if (view != null) { + view.setBackgroundTintColor(color); + } + } + public void closeRemoteInput() { for (NotificationContentView l : mLayouts) { l.closeRemoteInput(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java index 20e5f86ee097..1b613cbcaa41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java @@ -23,8 +23,10 @@ import android.service.notification.StatusBarNotification; import android.support.annotation.VisibleForTesting; import android.util.Log; +import com.android.internal.logging.MetricsLogger; import com.android.systemui.Dependency; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.notification.NotificationCounters; import com.android.systemui.statusbar.phone.StatusBar; import java.util.Collections; @@ -97,6 +99,9 @@ public class NotificationBlockingHelperManager { // We don't care about the touch origin (x, y) since we're opening guts without any // explicit user interaction. manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext)); + + Dependency.get(MetricsLogger.class) + .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1); return true; } return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 285f639224f6..8fa1b67518b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -887,6 +887,12 @@ public class NotificationContentView extends FrameLayout { mContainingNotification.setContentBackground(customBackgroundColor, animate, this); } + public void setBackgroundTintColor(int color) { + if (mExpandedSmartReplyView != null) { + mExpandedSmartReplyView.setBackgroundTintColor(color); + } + } + public int getVisibleType() { return mVisibleType; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index dff5f3814f68..46600cf71991 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -372,7 +372,7 @@ public class NotificationGutsManager implements Dumpable { @Override public void run() { if (row.getWindowToken() == null) { - Log.e(TAG, "Trying to show notification guts, but not attached to " + Log.e(TAG, "Trying to show notification guts in post(), but not attached to " + "window"); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index ec49f436b7d0..98e926807431 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -91,22 +91,24 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mIsForBlockingHelper; private boolean mNegativeUserSentiment; - /** Counter tag that describes how the user exit or quit out of this view. */ - private String mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED; + /** + * String that describes how the user exit or quit out of this view, also used as a counter tag. + */ + private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; private OnClickListener mOnKeepShowing = v -> { - mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; + mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING; closeControls(v); }; private OnClickListener mOnStopOrMinimizeNotifications = v -> { - mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; + mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; swapContent(false); }; private OnClickListener mOnUndo = v -> { // Reset exit counter that we'll log and record an undo event separately (not an exit event) - mExitReasonCounter = NotificationCounters.BLOCKING_HELPER_DISMISSED; + mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO); swapContent(true); }; @@ -197,8 +199,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G bindHeader(); bindPrompt(); bindButtons(); - - logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_SHOWN); } private void bindHeader() throws RemoteException { @@ -300,7 +300,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void saveImportance() { if (!mIsNonblockable) { - if (mCheckSaveListener != null) { + // Only go through the lock screen/bouncer if the user didn't hit 'Keep showing'. + if (mCheckSaveListener != null + && !NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING.equals(mExitReason)) { mCheckSaveListener.checkSave(this::updateImportance, mSbn); } else { updateImportance(); @@ -495,7 +497,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G if (save) { saveImportance(); } - logBlockingHelperCounter(mExitReasonCounter); + logBlockingHelperCounter(mExitReason); return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 19980a221576..04c500fc8f6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; import static com.android.systemui.statusbar.policy.DarkIconDispatcher.isInArea; @@ -24,10 +27,14 @@ import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.settingslib.graph.SignalDrawable; @@ -35,10 +42,14 @@ import com.android.systemui.R; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; -public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements DarkReceiver, +public class StatusBarMobileView extends FrameLayout implements DarkReceiver, StatusIconDisplayable { private static final String TAG = "StatusBarMobileView"; + /// Used to show etc dots + private StatusBarIconView mDotView; + /// The main icon view + private LinearLayout mMobileGroup; private String mSlot; private MobileIconState mState; private SignalDrawable mMobileDrawable; @@ -47,12 +58,17 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D private ImageView mOut; private ImageView mMobile, mMobileType, mMobileRoaming; private View mMobileRoamingSpace; + private int mVisibleState = -1; - public static StatusBarMobileView fromContext(Context context) { + public static StatusBarMobileView fromContext(Context context, String slot) { LayoutInflater inflater = LayoutInflater.from(context); - - return (StatusBarMobileView) + StatusBarMobileView v = (StatusBarMobileView) inflater.inflate(R.layout.status_bar_mobile_signal_group, null); + + v.setSlot(slot); + v.init(); + v.setVisibleState(STATE_ICON); + return v; } public StatusBarMobileView(Context context) { @@ -72,14 +88,8 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D super(context, attrs, defStyleAttr, defStyleRes); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - init(); - } - private void init() { + mMobileGroup = findViewById(R.id.mobile_group); mMobile = findViewById(R.id.mobile_signal); mMobileType = findViewById(R.id.mobile_type); mMobileRoaming = findViewById(R.id.mobile_roaming); @@ -90,6 +100,18 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D mMobileDrawable = new SignalDrawable(getContext()); mMobile.setImageDrawable(mMobileDrawable); + + initDotView(); + } + + private void initDotView() { + mDotView = new StatusBarIconView(mContext, mSlot, null); + mDotView.setVisibleState(STATE_DOT); + + int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size); + LayoutParams lp = new LayoutParams(width, width); + lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + addView(mDotView, lp); } public void applyMobileState(MobileIconState state) { @@ -113,9 +135,9 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D private void initViewState() { setContentDescription(mState.contentDescription); if (!mState.visible) { - setVisibility(View.GONE); + mMobileGroup.setVisibility(View.GONE); } else { - setVisibility(View.VISIBLE); + mMobileGroup.setVisibility(View.VISIBLE); } mMobileDrawable.setLevel(mState.strengthId); if (mState.typeId > 0) { @@ -137,7 +159,7 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D private void updateState(MobileIconState state) { setContentDescription(state.contentDescription); if (mState.visible != state.visible) { - setVisibility(state.visible ? View.VISIBLE : View.GONE); + mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE); } if (mState.strengthId != state.strengthId) { mMobileDrawable.setLevel(state.strengthId); @@ -173,6 +195,8 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D mOut.setImageTintList(color); mMobileType.setImageTintList(color); mMobileRoaming.setImageTintList(color); + mDotView.setDecorColor(tint); + mDotView.setIconColor(tint, false); } @Override @@ -194,11 +218,12 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D mOut.setImageTintList(list); mMobileType.setImageTintList(list); mMobileRoaming.setImageTintList(list); + mDotView.setDecorColor(color); } @Override public void setDecorColor(int color) { - //TODO: May also not be needed + mDotView.setDecorColor(color); } @Override @@ -208,12 +233,30 @@ public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements D @Override public void setVisibleState(int state) { - //TODO: May not be needed. Mobile is always expected to be visible (not a dot) + if (state == mVisibleState) { + return; + } + + mVisibleState = state; + switch (state) { + case STATE_ICON: + mMobileGroup.setVisibility(View.VISIBLE); + mDotView.setVisibility(View.GONE); + break; + case STATE_DOT: + mMobileGroup.setVisibility(View.INVISIBLE); + mDotView.setVisibility(View.VISIBLE); + break; + case STATE_HIDDEN: + default: + setVisibility(View.INVISIBLE); + break; + } } @Override public int getVisibleState() { - return 0; + return mVisibleState; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java index 3975ec2d2776..a09650867aa1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridGroupManager.java @@ -151,6 +151,17 @@ public class HybridGroupManager { return reusableView; } + public TextView bindOverflowNumberAmbient(TextView titleView, Notification notification, + int number) { + String text = mContext.getResources().getString( + R.string.notification_group_overflow_indicator_ambient, + resolveTitle(notification), number); + if (!text.equals(titleView.getText())) { + titleView.setText(text); + } + return titleView; + } + public void setOverflowNumberDark(TextView view, boolean dark, boolean fade, long delay) { mDozer.setIntensityDark((f)->{ mDarkAmount = f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 46b4078a1801..e0e991b4993f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -237,9 +237,8 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void addMobileView(MobileIconState state) { Log.d(TAG, "addMobileView: "); - StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot); - view.setSlot(state.slot); view.applyMobileState(state); view.setStaticDrawableColor(mColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index f13415103b9d..48eb3e84a4f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -76,7 +76,7 @@ public class KeyguardBouncer { protected KeyguardHostView mKeyguardView; private final Runnable mResetRunnable = ()-> { if (mKeyguardView != null) { - mKeyguardView.reset(); + mKeyguardView.resetSecurityContainer(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 533d5ecfab23..98f9f1abd301 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -88,6 +88,7 @@ import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABL import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW; +import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION; public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> { final static boolean DEBUG = false; @@ -116,6 +117,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private Rect mHomeButtonBounds = new Rect(); private Rect mBackButtonBounds = new Rect(); private Rect mRecentsButtonBounds = new Rect(); + private Rect mRotationButtonBounds = new Rect(); private int[] mTmpPosition = new int[2]; private KeyButtonDrawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon; @@ -341,6 +343,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mDownHitTarget = HIT_TARGET_HOME; } else if (mRecentsButtonBounds.contains(x, y)) { mDownHitTarget = HIT_TARGET_OVERVIEW; + } else if (mRotationButtonBounds.contains(x, y)) { + mDownHitTarget = HIT_TARGET_ROTATION; } break; } @@ -893,6 +897,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav updateButtonLocationOnScreen(getBackButton(), mBackButtonBounds); updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds); updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds); + updateButtonLocationOnScreen(getRotateSuggestionButton(), mRotationButtonBounds); mGestureHelper.onLayout(changed, left, top, right, bottom); mRecentsOnboarding.setNavBarHeight(getMeasuredHeight()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 061677c8d1c3..65cb56c7cc13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -249,7 +249,7 @@ import java.util.Map; public class StatusBar extends SystemUI implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, - OnHeadsUpChangedListener, CommandQueue.Callbacks, + OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback, ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter { public static final boolean MULTIUSER_DEBUG = false; @@ -785,12 +785,7 @@ public class StatusBar extends SystemUI implements DemoMode, // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel); mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); - mZenController.addCallback(new ZenModeController.Callback() { - @Override - public void onZenChanged(int zen) { - updateEmptyShadeView(); - } - }); + mZenController.addCallback(this); mActivityLaunchAnimator = new ActivityLaunchAnimator(mStatusBarWindow, this, mNotificationPanel, @@ -3376,6 +3371,7 @@ public class StatusBar extends SystemUI implements DemoMode, Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(null); mDeviceProvisionedController.removeCallback(mUserSetupObserver); Dependency.get(ConfigurationController.class).removeCallback(this); + mZenController.removeCallback(this); mAppOpsListener.destroy(); } @@ -5536,6 +5532,11 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override + public void onZenChanged(int zen) { + updateEmptyShadeView(); + } + + @Override public void showAssistDisclosure() { if (mAssistManager != null) { mAssistManager.showDisclosure(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 1ba37a9bf9dd..3b9ee8bcd378 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -296,8 +296,7 @@ public interface StatusBarIconController { } private StatusBarMobileView onCreateStatusBarMobileView(String slot) { - StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); - view.setSlot(slot); + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot); return view; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 4538977f7a6f..0811179e23d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -49,7 +49,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { private static final String TAG = "StatusIconContainer"; private static final boolean DEBUG = false; private static final boolean DEBUG_OVERFLOW = false; - // Max 5 status icons including battery + // Max 8 status icons including battery private static final int MAX_ICONS = 7; private static final int MAX_DOTS = 1; @@ -152,7 +152,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int visibleCount = mMeasureViews.size(); int maxVisible = visibleCount <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; - int totalWidth = getPaddingStart() + getPaddingEnd(); + int totalWidth = mPaddingLeft + mPaddingRight; boolean trackWidth = true; // Measure all children so that they report the correct width @@ -208,8 +208,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { */ private void calculateIconTranslations() { mLayoutStates.clear(); - float width = getWidth() - getPaddingEnd(); - float translationX = width; + float width = getWidth(); + float translationX = width - getPaddingEnd(); float contentStart = getPaddingStart(); int childCount = getChildCount(); // Underflow === don't show content until that index @@ -344,10 +344,11 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { animate = true; } - icon.setVisibleState(visibleState); if (animate) { animateTo(view, animationProperties); + icon.setVisibleState(visibleState); } else { + icon.setVisibleState(visibleState); super.applyToView(view); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 59bf982bcfdb..310f14c2fca7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -565,6 +565,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); + //if pinned, set imeOption to keep the behavior like in portrait. + if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isPinned()) { + outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI + | EditorInfo.IME_FLAG_NO_FULLSCREEN; + } if (mShowImeOnInputConnection && inputConnection != null) { final InputMethodManager imm = InputMethodManager.getInstance(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 351868dd8b7b..c279e6388592 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -1,12 +1,17 @@ package com.android.systemui.statusbar.policy; +import android.annotation.ColorInt; import android.app.PendingIntent; import android.app.RemoteInput; import android.content.Context; import android.content.Intent; +import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.RippleDrawable; import android.os.Bundle; import android.text.Layout; @@ -22,6 +27,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.Button; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.NotificationColorUtil; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -75,6 +81,23 @@ public class SmartReplyView extends ViewGroup { private View mSmartReplyContainer; + @ColorInt + private int mCurrentBackgroundColor; + @ColorInt + private final int mDefaultBackgroundColor; + @ColorInt + private final int mDefaultStrokeColor; + @ColorInt + private final int mDefaultTextColor; + @ColorInt + private final int mDefaultTextColorDarkBg; + @ColorInt + private final int mRippleColorDarkBg; + @ColorInt + private final int mRippleColor; + private final int mStrokeWidth; + private final double mMinStrokeContrast; + public SmartReplyView(Context context, AttributeSet attrs) { super(context, attrs); mConstants = Dependency.get(SmartReplyConstants.class); @@ -83,9 +106,21 @@ public class SmartReplyView extends ViewGroup { mHeightUpperLimit = NotificationUtils.getFontScaledHeight(mContext, R.dimen.smart_reply_button_max_height); + mCurrentBackgroundColor = context.getColor(R.color.smart_reply_button_background); + mDefaultBackgroundColor = mCurrentBackgroundColor; + mDefaultTextColor = mContext.getColor(R.color.smart_reply_button_text); + mDefaultTextColorDarkBg = mContext.getColor(R.color.smart_reply_button_text_dark_bg); + mDefaultStrokeColor = mContext.getColor(R.color.smart_reply_button_stroke); + mRippleColor = mContext.getColor(R.color.notification_ripple_untinted_color); + mRippleColorDarkBg = Color.argb(Color.alpha(mRippleColor), + 255 /* red */, 255 /* green */, 255 /* blue */); + mMinStrokeContrast = NotificationColorUtil.calculateContrast(mDefaultStrokeColor, + mDefaultBackgroundColor); + int spacing = 0; int singleLineButtonPaddingHorizontal = 0; int doubleLineButtonPaddingHorizontal = 0; + int strokeWidth = 0; final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.SmartReplyView, 0, 0); @@ -102,10 +137,14 @@ public class SmartReplyView extends ViewGroup { case R.styleable.SmartReplyView_doubleLineButtonPaddingHorizontal: doubleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0); break; + case R.styleable.SmartReplyView_buttonStrokeWidth: + strokeWidth = arr.getDimensionPixelSize(i, 0); + break; } } arr.recycle(); + mStrokeWidth = strokeWidth; mSpacing = spacing; mSingleLineButtonPaddingHorizontal = singleLineButtonPaddingHorizontal; mDoubleLineButtonPaddingHorizontal = doubleLineButtonPaddingHorizontal; @@ -139,6 +178,7 @@ public class SmartReplyView extends ViewGroup { View smartReplyContainer) { mSmartReplyContainer = smartReplyContainer; removeAllViews(); + mCurrentBackgroundColor = mDefaultBackgroundColor; if (remoteInput != null && pendingIntent != null) { CharSequence[] choices = remoteInput.getChoices(); if (choices != null) { @@ -194,6 +234,7 @@ public class SmartReplyView extends ViewGroup { } }); + setColors(b, mCurrentBackgroundColor, mDefaultStrokeColor, mDefaultTextColor, mRippleColor); return b; } @@ -523,6 +564,51 @@ public class SmartReplyView extends ViewGroup { return lp.show && super.drawChild(canvas, child, drawingTime); } + public void setBackgroundTintColor(int backgroundColor) { + if (backgroundColor == mCurrentBackgroundColor) { + // Same color ignoring. + return; + } + mCurrentBackgroundColor = backgroundColor; + + final boolean dark = !NotificationColorUtil.isColorLight(backgroundColor); + + int textColor = NotificationColorUtil.ensureTextContrast( + dark ? mDefaultTextColorDarkBg : mDefaultTextColor, + backgroundColor | 0xff000000, dark); + int strokeColor = NotificationColorUtil.ensureContrast( + mDefaultStrokeColor, backgroundColor | 0xff000000, dark, mMinStrokeContrast); + int rippleColor = dark ? mRippleColorDarkBg : mRippleColor; + + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final Button child = (Button) getChildAt(i); + setColors(child, backgroundColor, strokeColor, textColor, rippleColor); + } + } + + private void setColors(Button button, int backgroundColor, int strokeColor, int textColor, + int rippleColor) { + Drawable drawable = button.getBackground(); + if (drawable instanceof RippleDrawable) { + // Mutate in case other notifications are using this drawable. + drawable = drawable.mutate(); + RippleDrawable ripple = (RippleDrawable) drawable; + ripple.setColor(ColorStateList.valueOf(rippleColor)); + Drawable inset = ripple.getDrawable(0); + if (inset instanceof InsetDrawable) { + Drawable background = ((InsetDrawable) inset).getDrawable(); + if (background instanceof GradientDrawable) { + GradientDrawable gradientDrawable = (GradientDrawable) background; + gradientDrawable.setColor(backgroundColor); + gradientDrawable.setStroke(mStrokeWidth, strokeColor); + } + } + button.setBackground(drawable); + } + button.setTextColor(textColor); + } + @VisibleForTesting static class LayoutParams extends ViewGroup.LayoutParams { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 339c115c77a3..2031b27c93f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -36,6 +36,7 @@ import android.service.notification.Condition; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.ZenRule; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.qs.GlobalSetting; @@ -112,6 +113,10 @@ public class ZenModeControllerImpl extends CurrentUserTracker implements ZenMode @Override public void addCallback(Callback callback) { + if (callback == null) { + Slog.e(TAG, "Attempted to add a null callback."); + return; + } mCallbacks.add(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index 81152971645c..55ec142444f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -52,7 +52,7 @@ public class NotificationChildrenContainer extends ViewGroup { private static final int NUMBER_OF_CHILDREN_WHEN_COLLAPSED = 2; private static final int NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED = 5; private static final int NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED = 8; - private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 3; + private static final int NUMBER_OF_CHILDREN_WHEN_AMBIENT = 1; private static final AnimationProperties ALPHA_FADE_IN = new AnimationProperties() { private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @@ -208,9 +208,9 @@ public class NotificationChildrenContainer extends ViewGroup { // We need to measure all children even the GONE ones, such that the heights are // calculated correctly as they are used to calculate how many we can fit on the screen. boolean isOverflow = i == overflowIndex; - child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null - ? mOverflowNumber.getMeasuredWidth() - : 0); + child.setSingleLineWidthIndention(isOverflow && mOverflowNumber != null && + !mContainingNotification.isShowingAmbient() + ? mOverflowNumber.getMeasuredWidth() : 0); child.measure(widthMeasureSpec, newHeightSpec); // layout the divider View divider = mDividers.get(i); @@ -394,8 +394,19 @@ public class NotificationChildrenContainer extends ViewGroup { int childCount = mChildren.size(); int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */); if (childCount > maxAllowedVisibleChildren) { - mOverflowNumber = mHybridGroupManager.bindOverflowNumber( - mOverflowNumber, childCount - maxAllowedVisibleChildren); + int number = childCount - maxAllowedVisibleChildren; + mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number); + if (mContainingNotification.isShowingAmbient()) { + ExpandableNotificationRow overflowView = mChildren.get(0); + HybridNotificationView ambientSingleLineView = overflowView == null ? null + : overflowView.getAmbientSingleLineView(); + if (ambientSingleLineView != null) { + mHybridGroupManager.bindOverflowNumberAmbient( + ambientSingleLineView.getTitleView(), + mContainingNotification.getStatusBarNotification().getNotification(), + number); + } + } if (mGroupOverFlowState == null) { mGroupOverFlowState = new ViewState(); mNeverAppliedGroupState = true; @@ -617,16 +628,13 @@ public class NotificationChildrenContainer extends ViewGroup { } if (mOverflowNumber != null) { ExpandableNotificationRow overflowView = mChildren.get(Math.min( - getMaxAllowedVisibleChildren(true /* likeCollpased */), childCount) - 1); + getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1); mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView)); - if (mContainingNotification.isShowingAmbient() || !mChildrenExpanded) { - HybridNotificationView alignView = null; - if (mContainingNotification.isShowingAmbient()) { - alignView = overflowView.getAmbientSingleLineView(); - } else if (mUserLocked) { - alignView = overflowView.getSingleLineView(); - } + if (mContainingNotification.isShowingAmbient()) { + mGroupOverFlowState.alpha = 0.0f; + } else if (!mChildrenExpanded) { + HybridNotificationView alignView = overflowView.getSingleLineView(); if (alignView != null) { View mirrorView = alignView.getTextView(); if (mirrorView.getVisibility() == GONE) { @@ -635,9 +643,9 @@ public class NotificationChildrenContainer extends ViewGroup { if (mirrorView.getVisibility() == GONE) { mirrorView = alignView; } + mGroupOverFlowState.alpha = mirrorView.getAlpha(); mGroupOverFlowState.yTranslation += NotificationUtils.getRelativeYOffset( mirrorView, overflowView); - mGroupOverFlowState.alpha = mirrorView.getAlpha(); } } else { mGroupOverFlowState.yTranslation += mNotificationHeaderMargin; @@ -881,6 +889,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void notifyShowAmbientChanged() { updateHeaderVisibility(false); + updateGroupOverflow(); } private void updateHeaderVisibility(boolean animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 5deebffc4778..176905a31b9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -112,7 +112,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.function.BiConsumer; /** @@ -4078,23 +4077,20 @@ public class NotificationStackScrollLayout extends ViewGroup int newVisibility = visible ? VISIBLE : GONE; boolean changedVisibility = oldVisibility != newVisibility; - if (changedVisibility || newVisibility != GONE) { + if (changedVisibility) { if (newVisibility != GONE) { - int oldText = mEmptyShadeView.getTextResource(); - int newText; - if (mStatusBar.areNotificationsHidden()) { - newText = R.string.dnd_suppressing_shade_text; - } else { - newText = R.string.empty_shade_text; - } - if (changedVisibility || !Objects.equals(oldText, newText)) { - mEmptyShadeView.setText(newText); - showFooterView(mEmptyShadeView); - } + showFooterView(mEmptyShadeView); } else { hideFooterView(mEmptyShadeView, true); } } + + int oldTextRes = mEmptyShadeView.getTextResource(); + int newTextRes = mStatusBar.areNotificationsHidden() + ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text; + if (oldTextRes != newTextRes) { + mEmptyShadeView.setText(newTextRes); + } } public void updateFooterView(boolean visible, boolean showDismissView) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java index b3dddd522b1b..a6d87af17545 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java @@ -20,6 +20,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import android.content.Context; +import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -29,6 +30,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -48,6 +50,7 @@ import static org.mockito.Mockito.when; * Tests for {@link NotificationBlockingHelperManager}. */ @SmallTest +@FlakyTest @org.junit.runner.RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @@ -56,7 +59,6 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { private NotificationTestHelper mHelper; - @Rule public MockitoRule mockito = MockitoJUnit.rule(); @Mock private NotificationGutsManager mGutsManager; @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationMenuRow mMenuRow; @@ -64,20 +66,22 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Before public void setUp() { - mBlockingHelperManager = new NotificationBlockingHelperManager(mContext); - // By default, have the shade visible/expanded. - mBlockingHelperManager.setNotificationShadeExpanded(1f); - - mHelper = new NotificationTestHelper(mContext); + MockitoAnnotations.initMocks(this); when(mGutsManager.openGuts( any(View.class), anyInt(), anyInt(), any(NotificationMenuRowPlugin.MenuItem.class))) .thenReturn(true); + when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem); mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager); mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); - when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem); + + mHelper = new NotificationTestHelper(mContext); + + mBlockingHelperManager = new NotificationBlockingHelperManager(mContext); + // By default, have the shade visible/expanded. + mBlockingHelperManager.setNotificationShadeExpanded(1f); } @Test @@ -89,7 +93,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(createBlockableRowSpy()); + ExpandableNotificationRow row = createBlockableRowSpy(); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(false); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -102,7 +106,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(createBlockableRowSpy()); + ExpandableNotificationRow row = createBlockableRowSpy(); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(true); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -200,7 +204,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testBlockingHelperShowAndDismiss() throws Exception{ - ExpandableNotificationRow row = spy(createBlockableRowSpy()); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; when(row.isAttachedToWindow()).thenReturn(true); @@ -227,6 +231,4 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); return row; } - - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index 65fd7f5f3651..bdeb8bcd6fb8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -314,12 +314,11 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { - // Bind notification logs an event, so this counts as one invocation for the metrics logger. mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true, true); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); - verify(mMetricsLogger, times(2)).count(anyString(), anyInt()); + verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @Test @@ -509,6 +508,36 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), eq(true)); } + + @Test + public void testCloseControls_nonNullCheckSaveListenerDoesntDelayKeepShowing() + throws Exception { + NotificationInfo.CheckSaveListener listener = + mock(NotificationInfo.CheckSaveListener.class); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, + 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, + null /* onSettingsClick */, null /* onAppSettingsClick */ , + false /* isNonblockable */, true /* isForBlockingHelper */, + true /* isUserSentimentNegative */); + + NotificationGuts guts = spy(new NotificationGuts(mContext, null)); + when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); + doNothing().when(guts).animateClose(anyInt(), anyInt(), anyBoolean()); + doNothing().when(guts).setExposed(anyBoolean(), anyBoolean()); + guts.setGutsContent(mNotificationInfo); + mNotificationInfo.setGutsParent(guts); + + mNotificationInfo.findViewById(R.id.keep).performClick(); + + verify(mBlockingHelperManager).dismissCurrentBlockingHelper(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, times(1)) + .setNotificationsEnabledWithImportanceLockForPackage( + anyString(), eq(TEST_UID), eq(true)); + } + + @Test public void testCloseControls_blockingHelperDismissedIfShown() throws Exception { mNotificationInfo.bindNotification( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java index da8017e25525..ff6558700650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java @@ -105,4 +105,10 @@ public class ZenModeControllerImplTest extends SysuiTestCase { assertTrue(mController.areNotificationsHiddenInShade()); } -}
\ No newline at end of file + + @Test + public void testAddNullCallback() { + mController.addCallback(null); + mController.fireConfigChanged(null); + } +} diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 1541231befc6..7c05b2b00ff1 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5818,6 +5818,16 @@ message MetricsEvent { // Tag used to report whether an activity is being autofilled on compatibility mode. FIELD_AUTOFILL_COMPAT_MODE = 1414; + // OPEN: Settings > Sound > Switch a2dp devices dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_SWITCH_A2DP_DEVICES = 1415; + + // OPEN: Settings > Sound > Switch hfp devices dialog + // CATEGORY: SETTINGS + // OS: P + DIALOG_SWITCH_HFP_DEVICES = 1416; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 85b02206a594..f992049ef1fb 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -568,8 +568,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Context userContext = mContext.createPackageContextAsUser(providerPackage, 0, UserHandle.of(providerUserId)); PackageManager pm = userContext.getPackageManager(); - Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm); + Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate(); // Create a bitmap of the icon which is what the widget's remoteview requires. + icon.setColorFilter(mIconUtilities.getDisabledColorFilter()); return mIconUtilities.createIconBitmap(icon); } catch (NameNotFoundException e) { Slog.e(TAG, "Fail to get application icon", e); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 51c0488dcd9f..9b833f71869b 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -157,6 +157,9 @@ public final class AutofillManagerService extends SystemService { } }; + @GuardedBy("mLock") + private boolean mAllowInstantService; + public AutofillManagerService(Context context) { super(context); mContext = context; @@ -518,6 +521,23 @@ public final class AutofillManagerService extends SystemService { sFullScreenMode = mode; } + // Called by Shell command. + boolean getAllowInstantService() { + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); + synchronized (mLock) { + return mAllowInstantService; + } + } + + // Called by Shell command. + void setAllowInstantService(boolean mode) { + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); + Slog.i(TAG, "setAllowInstantService(): " + mode); + synchronized (mLock) { + mAllowInstantService = mode; + } + } + private void setDebugLocked(boolean debug) { com.android.server.autofill.Helper.sDebug = debug; android.view.autofill.Helper.sDebug = debug; @@ -866,7 +886,8 @@ public final class AutofillManagerService extends SystemService { synchronized (mLock) { final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); return service.startSessionLocked(activityToken, getCallingUid(), appCallback, - autofillId, bounds, value, hasCallback, flags, componentName, compatMode); + autofillId, bounds, value, hasCallback, componentName, compatMode, + mAllowInstantService, flags); } } @@ -1202,6 +1223,7 @@ public final class AutofillManagerService extends SystemService { mAutofillCompatState.dump(prefix2, pw); pw.print(prefix2); pw.print("from settings: "); pw.println(getWhitelistedCompatModePackagesFromSettings()); + pw.print("Allow instant service: "); pw.println(mAllowInstantService); } if (showHistory) { pw.println(); pw.println("Requests history:"); pw.println(); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 6ff9539ff5e6..d97253e1f5a8 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -32,6 +32,7 @@ import android.app.IActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; @@ -341,7 +342,8 @@ final class AutofillManagerServiceImpl { int startSessionLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback, - int flags, @NonNull ComponentName componentName, boolean compatMode) { + @NonNull ComponentName componentName, boolean compatMode, + boolean bindInstantServiceAllowed, int flags) { if (!isEnabledLocked()) { return 0; } @@ -371,7 +373,7 @@ final class AutofillManagerServiceImpl { pruneAbandonedSessionsLocked(); final Session newSession = createSessionByTokenLocked(activityToken, uid, appCallbackToken, - hasCallback, componentName, compatMode, flags); + hasCallback, componentName, compatMode, bindInstantServiceAllowed, flags); if (newSession == null) { return NO_SESSION; } @@ -490,7 +492,8 @@ final class AutofillManagerServiceImpl { @GuardedBy("mLock") private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid, @NonNull IBinder appCallbackToken, boolean hasCallback, - @NonNull ComponentName componentName, boolean compatMode, int flags) { + @NonNull ComponentName componentName, boolean compatMode, + boolean bindInstantServiceAllowed, int flags) { // use random ids so that one app cannot know that another app creates sessions int sessionId; int tries = 0; @@ -509,7 +512,7 @@ final class AutofillManagerServiceImpl { final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode, - flags); + bindInstantServiceAllowed, flags); mSessions.put(newSession.id, newSession); return newSession; @@ -667,7 +670,10 @@ final class AutofillManagerServiceImpl { @NonNull CharSequence getServiceLabel() { - return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()); + final CharSequence label = mInfo.getServiceInfo().loadSafeLabel( + mContext.getPackageManager(), 0 /* do not ellipsize */, + PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM); + return label; } @NonNull @@ -912,6 +918,7 @@ final class AutofillManagerServiceImpl { } else { pw.println(); mInfo.dump(prefix2, pw); + pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabel()); } pw.print(prefix); pw.print("Component from settings: "); pw.println(getComponentNameFromSettings()); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java index c76c8ac61705..f7b7ceb4a6da 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java @@ -86,6 +86,9 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" get fc_score [--algorithm ALGORITHM] value1 value2"); pw.println(" Gets the field classification score for 2 fields."); pw.println(""); + pw.println(" get bind-instant-service-allowed"); + pw.println(" Gets whether binding to services provided by instant apps is allowed"); + pw.println(""); pw.println(" set log_level [off | debug | verbose]"); pw.println(" Sets the Autofill log level."); pw.println(""); @@ -98,6 +101,9 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { pw.println(" set full_screen_mode [true | false | default]"); pw.println(" Sets the Fill UI full screen mode"); pw.println(""); + pw.println(" set bind-instant-service-allowed [true | false]"); + pw.println(" Sets whether binding to services provided by instant apps is allowed"); + pw.println(""); pw.println(" list sessions [--user USER_ID]"); pw.println(" Lists all pending sessions."); pw.println(""); @@ -123,6 +129,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return getFieldClassificationScore(pw); case "full_screen_mode": return getFullScreenMode(pw); + case "bind-instant-service-allowed": + return getBindInstantService(pw); default: pw.println("Invalid set: " + what); return -1; @@ -141,6 +149,8 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { return setMaxVisibileDatasets(); case "full_screen_mode": return setFullScreenMode(pw); + case "bind-instant-service-allowed": + return setBindInstantService(pw); default: pw.println("Invalid set: " + what); return -1; @@ -259,6 +269,30 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { } } + private int getBindInstantService(PrintWriter pw) { + if (mService.getAllowInstantService()) { + pw.println("true"); + } else { + pw.println("false"); + } + return 0; + } + + private int setBindInstantService(PrintWriter pw) { + final String mode = getNextArgRequired(); + switch (mode.toLowerCase()) { + case "true": + mService.setAllowInstantService(true); + return 0; + case "false": + mService.setAllowInstantService(false); + return 0; + default: + pw.println("Invalid mode: " + mode); + return -1; + } + } + private int requestDestroy(PrintWriter pw) { if (!isNextArgSessions(pw)) { return -1; diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java index 9bec856e2308..ba544f17c06c 100644 --- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java +++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java @@ -15,8 +15,6 @@ */ package com.android.server.autofill; -import static android.view.autofill.AutofillManager.FC_SERVICE_TIMEOUT; - import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; @@ -52,8 +50,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * Strategy used to bridge the field classification algorithms provided by a service in an external diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 5372d0c649cf..f78101338d2e 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -68,6 +68,9 @@ public final class Helper { /** * When non-null, overrides whether the UI should be shown on full-screen mode. + * + * <p>Note: access to this variable is not synchronized because it's "final" on real usage - + * it's only set by Shell cmd, for development purposes. */ public static Boolean sFullScreenMode = null; diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index f7a4b73e85cb..4ded3fe29f2a 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -84,6 +84,8 @@ final class RemoteFillService implements DeathRecipient { private final Handler mHandler; + private final boolean mBindInstantServiceAllowed; + private IAutoFillService mAutoFillService; private boolean mBinding; @@ -109,13 +111,14 @@ final class RemoteFillService implements DeathRecipient { } public RemoteFillService(Context context, ComponentName componentName, - int userId, FillServiceCallbacks callbacks) { + int userId, FillServiceCallbacks callbacks, boolean bindInstantServiceAllowed) { mContext = context; mCallbacks = callbacks; mComponentName = componentName; mIntent = new Intent(AutofillService.SERVICE_INTERFACE).setComponent(mComponentName); mUserId = userId; mHandler = new Handler(FgThread.getHandler().getLooper()); + mBindInstantServiceAllowed = bindInstantServiceAllowed; } public void destroy() { @@ -207,6 +210,7 @@ final class RemoteFillService implements DeathRecipient { .append(String.valueOf(isBound())).println(); pw.append(prefix).append(tab).append("hasPendingRequest=") .append(String.valueOf(mPendingRequest != null)).println(); + pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed); pw.println(); } @@ -258,12 +262,17 @@ final class RemoteFillService implements DeathRecipient { if (sVerbose) Slog.v(LOG_TAG, "[user: " + mUserId + "] ensureBound()"); mBinding = true; - boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + if (mBindInstantServiceAllowed) { + flags |= Context.BIND_ALLOW_INSTANT; + } + + final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, new UserHandle(mUserId)); if (!willBind) { - if (sDebug) Slog.d(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent); + Slog.w(LOG_TAG, "[user: " + mUserId + "] could not bind to " + mIntent + " using flags " + + flags); mBinding = false; if (!mServiceDied) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 88a679b43cc6..73c71725200c 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -517,7 +517,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, @NonNull LocalLog wtfHistory, @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName, - boolean compatMode, int flags) { + boolean compatMode, boolean bindInstantServiceAllowed, int flags) { id = sessionId; mFlags = flags; this.uid = uid; @@ -526,7 +526,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mLock = lock; mUi = ui; mHandler = handler; - mRemoteFillService = new RemoteFillService(context, serviceComponentName, userId, this); + mRemoteFillService = new RemoteFillService(context, serviceComponentName, userId, this, + bindInstantServiceAllowed); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index 2a7605552b9b..cff1a84ceafe 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -18,7 +18,6 @@ package com.android.server.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static com.android.server.autofill.Helper.sDebug; -import static com.android.server.autofill.Helper.sVerbose; import android.annotation.Nullable; import android.graphics.Rect; diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 5c5f0f8d3b4b..0775abf574b5 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -3023,20 +3023,26 @@ class AlarmManagerService extends SystemService { } private boolean isBackgroundRestricted(Alarm alarm) { - final boolean allowWhileIdle = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0; + boolean exemptOnBatterySaver = (alarm.flags & FLAG_ALLOW_WHILE_IDLE) != 0; if (alarm.alarmClock != null) { - // Don't block alarm clocks + // Don't defer alarm clocks return false; } - if (alarm.operation != null - && (alarm.operation.isActivity() || alarm.operation.isForegroundService())) { - // Don't block starting foreground components - return false; + if (alarm.operation != null) { + if (alarm.operation.isActivity()) { + // Don't defer starting actual UI + return false; + } + if (alarm.operation.isForegroundService()) { + // FG service alarms are nearly as important; consult AST policy + exemptOnBatterySaver = true; + } } final String sourcePackage = alarm.sourcePackage; final int sourceUid = alarm.creatorUid; return (mAppStateTracker != null) && - mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, allowWhileIdle); + mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, + exemptOnBatterySaver); } private native long init(); diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 13503e60183f..7f26575651af 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -21,13 +21,16 @@ import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; +import android.database.ContentObserver; import android.media.AudioAttributes; +import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; @@ -43,9 +46,11 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManagerInternal; +import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.KeyValueListParser; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -109,9 +114,6 @@ public class AppOpsService extends IAppOpsService.Stub { // Write at most every 30 minutes. static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000; - // How long we want for a drop in uid state to settle before applying it. - static final long STATE_SETTLE_TIME = 10*1000; - // Constant meaning that any UID should be matched when dispatching callbacks private static final int UID_ANY = -2; @@ -198,6 +200,75 @@ public class AppOpsService extends IAppOpsService.Stub { */ private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>(); + /** + * All times are in milliseconds. These constants are kept synchronized with the system + * global Settings. Any access to this class or its fields should be done while + * holding the AppOpsService lock. + */ + private final class Constants extends ContentObserver { + // Key names stored in the settings value. + private static final String KEY_STATE_SETTLE_TIME = "state_settle_time"; + + /** + * How long we want for a drop in uid state to settle before applying it. + * @see Settings.Global#APP_OPS_CONSTANTS + * @see #KEY_STATE_SETTLE_TIME + */ + public long STATE_SETTLE_TIME; + + + private final KeyValueListParser mParser = new KeyValueListParser(','); + private ContentResolver mResolver; + + public Constants(Handler handler) { + super(handler); + updateConstants(); + } + + public void startMonitoring(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS), + false, this); + updateConstants(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateConstants(); + } + + private void updateConstants() { + synchronized (AppOpsService.this) { + try { + if (mResolver != null) { + mParser.setString(Settings.Global.getString(mResolver, + Settings.Global.APP_OPS_CONSTANTS)); + } else { + mParser.setString(""); + } + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad app ops settings", e); + } + + STATE_SETTLE_TIME = mParser.getDurationMillis( + KEY_STATE_SETTLE_TIME, 10 * 1000L); + } + } + + void dump(PrintWriter pw) { + pw.println(" Settings:"); + + pw.print(" "); pw.print(KEY_STATE_SETTLE_TIME); pw.print("="); + TimeUtils.formatDuration(STATE_SETTLE_TIME, pw); + pw.println(); + } + } + + private final Constants mConstants; + @VisibleForTesting static final class UidState { public final int uid; @@ -210,7 +281,9 @@ public class AppOpsService extends IAppOpsService.Stub { public ArrayMap<String, Ops> pkgOps; public SparseIntArray opModes; + // true indicates there is an interested observer, false there isn't but it has such an op public SparseBooleanArray foregroundOps; + public boolean hasForegroundWatchers; public UidState(int uid) { this.uid = uid; @@ -234,8 +307,35 @@ public class AppOpsService extends IAppOpsService.Stub { return mode; } - public void evalForegroundOps() { + private void evalForegroundWatchers(int op, SparseArray<ArraySet<ModeCallback>> watchers, + SparseBooleanArray which) { + boolean curValue = which.get(op, false); + ArraySet<ModeCallback> callbacks = watchers.get(op); + if (callbacks != null) { + for (int cbi = callbacks.size() - 1; !curValue && cbi >= 0; cbi--) { + if ((callbacks.valueAt(cbi).mFlags + & AppOpsManager.WATCH_FOREGROUND_CHANGES) != 0) { + hasForegroundWatchers = true; + curValue = true; + } + } + } + which.put(op, curValue); + } + + public void evalForegroundOps(SparseArray<ArraySet<ModeCallback>> watchers) { SparseBooleanArray which = null; + hasForegroundWatchers = false; + if (opModes != null) { + for (int i = opModes.size() - 1; i >= 0; i--) { + if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) { + if (which == null) { + which = new SparseBooleanArray(); + } + evalForegroundWatchers(opModes.keyAt(i), watchers, which); + } + } + } if (pkgOps != null) { for (int i = pkgOps.size() - 1; i >= 0; i--) { Ops ops = pkgOps.valueAt(i); @@ -244,7 +344,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (which == null) { which = new SparseBooleanArray(); } - which.put(ops.keyAt(j), true); + evalForegroundWatchers(ops.keyAt(j), watchers, which); } } } @@ -313,13 +413,15 @@ public class AppOpsService extends IAppOpsService.Stub { final class ModeCallback implements DeathRecipient { final IAppOpsCallback mCallback; final int mWatchingUid; + final int mFlags; final int mCallingUid; final int mCallingPid; - ModeCallback(IAppOpsCallback callback, int watchingUid, int callingUid, + ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int callingUid, int callingPid) { mCallback = callback; mWatchingUid = watchingUid; + mFlags = flags; mCallingUid = callingUid; mCallingPid = callingPid; try { @@ -328,6 +430,10 @@ public class AppOpsService extends IAppOpsService.Stub { } } + public boolean isWatchingUid(int uid) { + return uid == UID_ANY || mWatchingUid < 0 || mWatchingUid == uid; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(128); @@ -335,6 +441,8 @@ public class AppOpsService extends IAppOpsService.Stub { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" watchinguid="); UserHandle.formatUid(sb, mWatchingUid); + sb.append(" flags=0x"); + sb.append(Integer.toHexString(mFlags)); sb.append(" from uid="); UserHandle.formatUid(sb, mCallingUid); sb.append(" pid="); @@ -439,6 +547,7 @@ public class AppOpsService extends IAppOpsService.Stub { LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mFile = new AtomicFile(storagePath, "appops"); mHandler = handler; + mConstants = new Constants(mHandler); readState(); } @@ -449,6 +558,7 @@ public class AppOpsService extends IAppOpsService.Stub { public void systemReady() { synchronized (this) { + mConstants.startMonitoring(mContext.getContentResolver()); boolean changed = false; for (int i = mUidStates.size() - 1; i >= 0; i--) { UidState uidState = mUidStates.valueAt(i); @@ -611,14 +721,16 @@ public class AppOpsService extends IAppOpsService.Stub { final UidState uidState = getUidStateLocked(uid, true); final int newState = PROCESS_STATE_TO_UID_STATE[procState]; if (uidState != null && uidState.pendingState != newState) { + final int oldPendingState = uidState.pendingState; + uidState.pendingState = newState; if (newState < uidState.state) { // We are moving to a more important state, always do it immediately. - uidState.state = newState; - uidState.pendingStateCommitTime = 0; + commitUidPendingStateLocked(uidState); } else if (uidState.pendingStateCommitTime == 0) { // We are moving to a less important state for the first time, // delay the application for a bit. - uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + STATE_SETTLE_TIME; + uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + + mConstants.STATE_SETTLE_TIME; } if (uidState.startNesting != 0) { // There is some actively running operation... need to find it @@ -629,13 +741,12 @@ public class AppOpsService extends IAppOpsService.Stub { for (int j = ops.size() - 1; j >= 0; j--) { final Op op = ops.valueAt(j); if (op.startNesting > 0) { - op.time[uidState.pendingState] = now; + op.time[oldPendingState] = now; op.time[newState] = now; } } } } - uidState.pendingState = newState; } } } @@ -867,7 +978,9 @@ public class AppOpsService extends IAppOpsService.Stub { ModeCallback callback = callbacks.valueAt(i); ArraySet<String> changedPackages = new ArraySet<>(); Collections.addAll(changedPackages, uidPackageNames); - callbackSpecs = new ArrayMap<>(); + if (callbackSpecs == null) { + callbackSpecs = new ArrayMap<>(); + } callbackSpecs.put(callback, changedPackages); } } @@ -932,7 +1045,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (op.mode != mode) { op.mode = mode; if (uidState != null) { - uidState.evalForegroundOps(); + uidState.evalForegroundOps(mOpModeWatchers); } ArraySet<ModeCallback> cbs = mOpModeWatchers.get(code); if (cbs != null) { @@ -1126,7 +1239,7 @@ public class AppOpsService extends IAppOpsService.Stub { mUidStates.remove(uidState.uid); } if (uidChanged) { - uidState.evalForegroundOps(); + uidState.evalForegroundOps(mOpModeWatchers); } } @@ -1148,8 +1261,23 @@ public class AppOpsService extends IAppOpsService.Stub { } } + private void evalAllForegroundOpsLocked() { + for (int uidi = mUidStates.size() - 1; uidi >= 0; uidi--) { + final UidState uidState = mUidStates.valueAt(uidi); + if (uidState.foregroundOps != null) { + uidState.evalForegroundOps(mOpModeWatchers); + } + } + } + @Override public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) { + startWatchingModeWithFlags(op, packageName, 0, callback); + } + + @Override + public void startWatchingModeWithFlags(int op, String packageName, int flags, + IAppOpsCallback callback) { int watchedUid = -1; final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); @@ -1166,7 +1294,7 @@ public class AppOpsService extends IAppOpsService.Stub { op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op; ModeCallback cb = mModeWatchers.get(callback.asBinder()); if (cb == null) { - cb = new ModeCallback(callback, watchedUid, callingUid, callingPid); + cb = new ModeCallback(callback, watchedUid, flags, callingUid, callingPid); mModeWatchers.put(callback.asBinder(), cb); } if (op != AppOpsManager.OP_NONE) { @@ -1185,6 +1313,7 @@ public class AppOpsService extends IAppOpsService.Stub { } cbs.add(cb); } + evalAllForegroundOpsLocked(); } } @@ -1212,6 +1341,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } } + evalAllForegroundOpsLocked(); } } @@ -1249,7 +1379,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (op == null) { return AppOpsManager.opToDefaultMode(code); } - return op.mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : op.mode; + return op.mode; } } @@ -1699,13 +1829,11 @@ public class AppOpsService extends IAppOpsService.Stub { } else { if (uidState.pendingStateCommitTime != 0) { if (uidState.pendingStateCommitTime < mLastUptime) { - uidState.state = uidState.pendingState; - uidState.pendingStateCommitTime = 0; + commitUidPendingStateLocked(uidState); } else { mLastUptime = SystemClock.uptimeMillis(); if (uidState.pendingStateCommitTime < mLastUptime) { - uidState.state = uidState.pendingState; - uidState.pendingStateCommitTime = 0; + commitUidPendingStateLocked(uidState); } } } @@ -1713,6 +1841,44 @@ public class AppOpsService extends IAppOpsService.Stub { return uidState; } + private void commitUidPendingStateLocked(UidState uidState) { + uidState.state = uidState.pendingState; + uidState.pendingStateCommitTime = 0; + if (uidState.hasForegroundWatchers) { + for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) { + if (!uidState.foregroundOps.valueAt(fgi)) { + continue; + } + final int code = uidState.foregroundOps.keyAt(fgi); + + final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); + if (callbacks != null) { + for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) { + final ModeCallback callback = callbacks.valueAt(cbi); + if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0 + || !callback.isWatchingUid(uidState.uid)) { + continue; + } + boolean doAllPackages = uidState.opModes != null + && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND; + if (uidState.pkgOps != null) { + for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) { + final Op op = uidState.pkgOps.valueAt(pkgi).get(code); + if (doAllPackages || (op != null + && op.mode == AppOpsManager.MODE_FOREGROUND)) { + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChanged, + this, callback, code, uidState.uid, + uidState.pkgOps.keyAt(pkgi))); + } + } + } + } + } + } + } + } + private Ops getOpsRawLocked(int uid, String packageName, boolean edit, boolean uidMismatchExpected) { UidState uidState = getUidStateLocked(uid, edit); @@ -1952,7 +2118,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (changed) { - uidState.evalForegroundOps(); + uidState.evalForegroundOps(mOpModeWatchers); } } } @@ -2151,7 +2317,7 @@ public class AppOpsService extends IAppOpsService.Stub { } UidState uidState = getUidStateLocked(uid, false); if (uidState != null) { - uidState.evalForegroundOps(); + uidState.evalForegroundOps(mOpModeWatchers); } } @@ -2322,7 +2488,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - int strModeToMode(String modeStr, PrintWriter err) { + static int strModeToMode(String modeStr, PrintWriter err) { for (int i = AppOpsManager.MODE_NAMES.length - 1; i >= 0; i--) { if (AppOpsManager.MODE_NAMES[i].equals(modeStr)) { return i; @@ -2716,6 +2882,10 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Print this help text."); pw.println(" --op [OP]"); pw.println(" Limit output to data associated with the given app op code."); + pw.println(" --mode [MODE]"); + pw.println(" Limit output to data associated with the given app op mode."); + pw.println(" --package [PACKAGE]"); + pw.println(" Limit output to data associated with the given package name."); } private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times, @@ -2751,6 +2921,9 @@ public class AppOpsService extends IAppOpsService.Stub { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; int dumpOp = -1; + String dumpPackage = null; + int dumpUid = -1; + int dumpMode = -1; if (args != null) { for (int i=0; i<args.length; i++) { @@ -2770,6 +2943,34 @@ public class AppOpsService extends IAppOpsService.Stub { if (dumpOp < 0) { return; } + } else if ("--package".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("No argument for --package option"); + return; + } + dumpPackage = args[i]; + try { + dumpUid = AppGlobals.getPackageManager().getPackageUid(dumpPackage, + PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_INSTANT, + 0); + } catch (RemoteException e) { + } + if (dumpUid < 0) { + pw.println("Unknown package: " + dumpPackage); + return; + } + dumpUid = UserHandle.getAppId(dumpUid); + } else if ("--mode".equals(arg)) { + i++; + if (i >= args.length) { + pw.println("No argument for --mode option"); + return; + } + dumpMode = Shell.strModeToMode(args[i], pw); + if (dumpMode < 0) { + return; + } } else if (arg.length() > 0 && arg.charAt(0) == '-'){ pw.println("Unknown option: " + arg); return; @@ -2782,6 +2983,8 @@ public class AppOpsService extends IAppOpsService.Stub { synchronized (this) { pw.println("Current AppOps Service state:"); + mConstants.dump(pw); + pw.println(); final long now = System.currentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); final long nowUptime = SystemClock.uptimeMillis(); @@ -2789,29 +2992,46 @@ public class AppOpsService extends IAppOpsService.Stub { final Date date = new Date(); boolean needSep = false; if (mOpModeWatchers.size() > 0) { - needSep = true; boolean printedHeader = false; for (int i=0; i<mOpModeWatchers.size(); i++) { if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) { continue; } - if (!printedHeader) { - pw.println(" Op mode watchers:"); - printedHeader = true; - } - pw.print(" Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); - pw.println(":"); + boolean printedOpHeader = false; ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i); for (int j=0; j<callbacks.size(); j++) { + final ModeCallback cb = callbacks.valueAt(j); + if (dumpPackage != null && cb.mWatchingUid >= 0 + && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { + continue; + } + needSep = true; + if (!printedHeader) { + pw.println(" Op mode watchers:"); + printedHeader = true; + } + if (!printedOpHeader) { + pw.print(" Op "); + pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i))); + pw.println(":"); + printedOpHeader = true; + } pw.print(" #"); pw.print(j); pw.print(": "); - pw.println(callbacks.valueAt(j)); + pw.println(cb); } } } - if (mPackageModeWatchers.size() > 0) { - needSep = true; - pw.println(" Package mode watchers:"); + if (mPackageModeWatchers.size() > 0 && dumpOp < 0) { + boolean printedHeader = false; for (int i=0; i<mPackageModeWatchers.size(); i++) { + if (dumpPackage != null && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) { + continue; + } + needSep = true; + if (!printedHeader) { + pw.println(" Package mode watchers:"); + printedHeader = true; + } pw.print(" Pkg "); pw.print(mPackageModeWatchers.keyAt(i)); pw.println(":"); ArraySet<ModeCallback> callbacks = mPackageModeWatchers.valueAt(i); @@ -2822,15 +3042,24 @@ public class AppOpsService extends IAppOpsService.Stub { } } if (mModeWatchers.size() > 0 && dumpOp < 0) { - needSep = true; - pw.println(" All op mode watchers:"); + boolean printedHeader = false; for (int i=0; i<mModeWatchers.size(); i++) { + final ModeCallback cb = mModeWatchers.valueAt(i); + if (dumpPackage != null && cb.mWatchingUid >= 0 + && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { + continue; + } + needSep = true; + if (!printedHeader) { + pw.println(" All op mode watchers:"); + printedHeader = true; + } pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(mModeWatchers.keyAt(i)))); - pw.print(": "); pw.println(mModeWatchers.valueAt(i)); + pw.print(": "); pw.println(cb); } } - if (mActiveWatchers.size() > 0) { + if (mActiveWatchers.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; for (int i = 0; i < mActiveWatchers.size(); i++) { @@ -2838,9 +3067,14 @@ public class AppOpsService extends IAppOpsService.Stub { if (activeWatchers.size() <= 0) { continue; } + final ActiveCallback cb = activeWatchers.valueAt(0); if (dumpOp >= 0 && activeWatchers.indexOfKey(dumpOp) < 0) { continue; } + if (dumpPackage != null && cb.mWatchingUid >= 0 + && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) { + continue; + } if (!printedHeader) { pw.println(" All op active watchers:"); printedHeader = true; @@ -2862,10 +3096,10 @@ public class AppOpsService extends IAppOpsService.Stub { } pw.println("]"); pw.print(" "); - pw.println(activeWatchers.valueAt(0)); + pw.println(cb); } } - if (mClients.size() > 0) { + if (mClients.size() > 0 && dumpMode < 0) { needSep = true; boolean printedHeader = false; for (int i=0; i<mClients.size(); i++) { @@ -2878,6 +3112,9 @@ public class AppOpsService extends IAppOpsService.Stub { if (dumpOp >= 0 && op.op != dumpOp) { continue; } + if (dumpPackage != null && !dumpPackage.equals(op.packageName)) { + continue; + } if (!printedHeader) { pw.println(" Clients:"); printedHeader = true; @@ -2898,7 +3135,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - if (mAudioRestrictions.size() > 0 && dumpOp < 0) { + if (mAudioRestrictions.size() > 0 && dumpOp < 0 && dumpPackage != null + && dumpMode < 0) { boolean printedHeader = false; for (int o=0; o<mAudioRestrictions.size(); o++) { final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o)); @@ -2931,19 +3169,44 @@ public class AppOpsService extends IAppOpsService.Stub { final SparseIntArray opModes = uidState.opModes; final ArrayMap<String, Ops> pkgOps = uidState.pkgOps; - if (dumpOp >= 0) { - boolean hasOp = uidState.opModes != null - && uidState.opModes.indexOfKey(dumpOp) >= 0; + if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) { + boolean hasOp = dumpOp < 0 || (uidState.opModes != null + && uidState.opModes.indexOfKey(dumpOp) >= 0); + boolean hasPackage = dumpPackage == null; + boolean hasMode = dumpMode < 0; + if (!hasMode && opModes != null) { + for (int opi = 0; !hasMode && opi < opModes.size(); opi++) { + if (opModes.valueAt(opi) == dumpMode) { + hasMode = true; + } + } + } if (pkgOps != null) { - for (int pkgi = 0; !hasOp && pkgi < pkgOps.size(); pkgi++) { + for (int pkgi = 0; + (!hasOp || !hasPackage || !hasMode) && pkgi < pkgOps.size(); + pkgi++) { Ops ops = pkgOps.valueAt(pkgi); - if (ops != null && ops.indexOfKey(dumpOp) >= 0) { + if (!hasOp && ops != null && ops.indexOfKey(dumpOp) >= 0) { hasOp = true; - continue; } + if (!hasMode) { + for (int opi = 0; !hasMode && opi < ops.size(); opi++) { + if (ops.valueAt(opi).mode == dumpMode) { + hasMode = true; + } + } + } + if (!hasPackage && dumpPackage.equals(ops.packageName)) { + hasPackage = true; + } + } + } + if (uidState.foregroundOps != null && !hasOp) { + if (uidState.foregroundOps.indexOfKey(dumpOp) > 0) { + hasOp = true; } } - if (!hasOp) { + if (!hasOp || !hasPackage || !hasMode) { continue; } } @@ -2964,12 +3227,20 @@ public class AppOpsService extends IAppOpsService.Stub { pw.print(" startNesting="); pw.println(uidState.startNesting); } - if (uidState.foregroundOps != null) { + if (uidState.foregroundOps != null && (dumpMode < 0 + || dumpMode == AppOpsManager.MODE_FOREGROUND)) { pw.println(" foregroundOps:"); for (int j = 0; j < uidState.foregroundOps.size(); j++) { - pw.print(" "); - pw.println(AppOpsManager.opToName(uidState.foregroundOps.keyAt(j))); + if (dumpOp >= 0 && dumpOp != uidState.foregroundOps.keyAt(j)) { + continue; + } + pw.print(" "); + pw.print(AppOpsManager.opToName(uidState.foregroundOps.keyAt(j))); + pw.print(": "); + pw.println(uidState.foregroundOps.valueAt(j) ? "WATCHER" : "SILENT"); } + pw.print(" hasForegroundWatchers="); + pw.println(uidState.hasForegroundWatchers); } needSep = true; @@ -2981,6 +3252,9 @@ public class AppOpsService extends IAppOpsService.Stub { if (dumpOp >= 0 && dumpOp != code) { continue; } + if (dumpMode >= 0 && dumpMode != mode) { + continue; + } pw.print(" "); pw.print(AppOpsManager.opToName(code)); pw.print(": mode="); pw.println(AppOpsManager.modeToName(mode)); } @@ -2991,19 +3265,34 @@ public class AppOpsService extends IAppOpsService.Stub { } for (int pkgi = 0; pkgi < pkgOps.size(); pkgi++) { - Ops ops = pkgOps.valueAt(pkgi); + final Ops ops = pkgOps.valueAt(pkgi); + if (dumpPackage != null && !dumpPackage.equals(ops.packageName)) { + continue; + } boolean printedPackage = false; for (int j=0; j<ops.size(); j++) { - Op op = ops.valueAt(j); + final Op op = ops.valueAt(j); if (dumpOp >= 0 && dumpOp != op.op) { continue; } + if (dumpMode >= 0 && dumpMode != op.mode) { + continue; + } if (!printedPackage) { pw.print(" Package "); pw.print(ops.packageName); pw.println(":"); printedPackage = true; } pw.print(" "); pw.print(AppOpsManager.opToName(op.op)); pw.print(" ("); pw.print(AppOpsManager.modeToName(op.mode)); + final int switchOp = AppOpsManager.opToSwitch(op.op); + if (switchOp != op.op) { + pw.print(" / switch "); + pw.print(AppOpsManager.opToName(switchOp)); + final Op switchObj = ops.get(switchOp); + int mode = switchObj != null + ? switchObj.mode : AppOpsManager.opToDefaultMode(switchOp); + pw.print("="); pw.print(AppOpsManager.modeToName(mode)); + } pw.println("): "); dumpTimesLocked(pw, " Access: ", diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java index 23c57797c1f5..9b001ce3f917 100644 --- a/services/core/java/com/android/server/AppStateTracker.java +++ b/services/core/java/com/android/server/AppStateTracker.java @@ -1056,9 +1056,9 @@ public class AppStateTracker { * @return whether alarms should be restricted for a UID package-name. */ public boolean areAlarmsRestricted(int uid, @NonNull String packageName, - boolean allowWhileIdle) { + boolean isExemptOnBatterySaver) { return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false, - /* exemptOnBatterySaver =*/ allowWhileIdle); + isExemptOnBatterySaver); } /** diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 72f9d749865b..797cb4b53ecb 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -500,24 +500,24 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int MAX_VALIDATION_LOGS = 10; private static class ValidationLog { final Network mNetwork; - final String mNetworkExtraInfo; + final String mName; final ReadOnlyLocalLog mLog; - ValidationLog(Network network, String networkExtraInfo, ReadOnlyLocalLog log) { + ValidationLog(Network network, String name, ReadOnlyLocalLog log) { mNetwork = network; - mNetworkExtraInfo = networkExtraInfo; + mName = name; mLog = log; } } private final ArrayDeque<ValidationLog> mValidationLogs = new ArrayDeque<ValidationLog>(MAX_VALIDATION_LOGS); - private void addValidationLogs(ReadOnlyLocalLog log, Network network, String networkExtraInfo) { + private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) { synchronized (mValidationLogs) { while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { mValidationLogs.removeLast(); } - mValidationLogs.addFirst(new ValidationLog(network, networkExtraInfo, log)); + mValidationLogs.addFirst(new ValidationLog(network, name, log)); } } @@ -2097,7 +2097,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (mValidationLogs) { pw.println("mValidationLogs (most recent first):"); for (ValidationLog p : mValidationLogs) { - pw.println(p.mNetwork + " - " + p.mNetworkExtraInfo); + pw.println(p.mNetwork + " - " + p.mName); pw.increaseIndent(); p.mLog.dump(fd, pw, args); pw.decreaseIndent(); @@ -4628,8 +4628,10 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (this) { nai.networkMonitor.systemReady = mSystemReady; } - addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, - networkInfo.getExtraInfo()); + final String extraInfo = networkInfo.getExtraInfo(); + final String name = TextUtils.isEmpty(extraInfo) + ? nai.networkCapabilities.getSSID() : extraInfo; + addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name); if (DBG) log("registerNetworkAgent " + nai); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); return nai.network.netId; diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 33ca02fecb70..60f1877d3739 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -107,7 +107,6 @@ public class IpSecService extends IIpSecService.Stub { static final int FREE_PORT_MIN = 1024; // ports 1-1023 are reserved static final int PORT_MAX = 0xFFFF; // ports are an unsigned 16-bit integer - static final String TUNNEL_INTERFACE_PREFIX = "ipsec"; /* Binder context for this service */ private final Context mContext; @@ -1270,7 +1269,7 @@ public class IpSecService extends IIpSecService.Stub { final int resourceId = mNextResourceId++; final int ikey = reserveNetId(); final int okey = reserveNetId(); - String intfName = String.format("%s%d", TUNNEL_INTERFACE_PREFIX, resourceId); + String intfName = String.format("%s%d", INetd.IPSEC_INTERFACE_PREFIX, resourceId); try { // Calls to netd: diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 58b40b1a5f6b..48090f215921 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -466,6 +466,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.dex.DexManager; import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.PinnedStackWindowController; @@ -4311,7 +4312,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (app.info.isPrivilegedApp() && - SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) { + DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) { runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES; } @@ -22994,18 +22995,27 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, + private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, boolean doingAll, long now) { if (mAdjSeq == app.adjSeq) { - // This adjustment has already been computed. - return app.curRawAdj; + if (app.adjSeq == app.completedAdjSeq) { + // This adjustment has already been computed successfully. + return false; + } else { + // The process is being computed, so there is a cycle. We cannot + // rely on this process's state. + app.containsCycle = true; + return false; + } } if (app.thread == null) { app.adjSeq = mAdjSeq; app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND; app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; - return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ); + app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ; + app.completedAdjSeq = app.adjSeq; + return false; } app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; @@ -23018,6 +23028,8 @@ public class ActivityManagerService extends IActivityManager.Stub final int appUid = app.info.uid; final int logUid = mCurOomAdjUid; + int prevAppAdj = app.curAdj; + if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) { // The max adjustment doesn't allow this app to be anything // below foreground, so it is not worth doing work for it. @@ -23062,7 +23074,10 @@ public class ActivityManagerService extends IActivityManager.Stub app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED; } } - return (app.curAdj=app.maxAdj); + app.curAdj = app.maxAdj; + app.completedAdjSeq = app.adjSeq; + // if curAdj is less than prevAppAdj, then this process was promoted + return app.curAdj < prevAppAdj; } app.systemNoUi = false; @@ -23074,6 +23089,8 @@ public class ActivityManagerService extends IActivityManager.Stub int adj; int schedGroup; int procState; + int cachedAdjSeq; + boolean foregroundActivities = false; mTmpBroadcastQueue.clear(); if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) { @@ -23387,9 +23404,9 @@ public class ActivityManagerService extends IActivityManager.Stub // there are applications dependent on our services or providers, but // this gives us a baseline and makes sure we don't get into an // infinite recursion. - app.adjSeq = mAdjSeq; app.curRawAdj = adj; app.hasStartedServices = false; + app.adjSeq = mAdjSeq; if (mBackupTarget != null && app == mBackupTarget.app) { // If possible we want to avoid killing apps while they're being backed up @@ -23488,8 +23505,15 @@ public class ActivityManagerService extends IActivityManager.Stub if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { ProcessRecord client = cr.binding.client; - int clientAdj = computeOomAdjLocked(client, cachedAdj, - TOP_APP, doingAll, now); + computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); + if (client.containsCycle) { + // We've detected a cycle. We should ignore this connection and allow + // this process to retry computeOomAdjLocked later in case a later-checked + // connection from a client would raise its priority legitimately. + app.containsCycle = true; + continue; + } + int clientAdj = client.curRawAdj; int clientProcState = client.curProcState; if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here @@ -23708,7 +23732,15 @@ public class ActivityManagerService extends IActivityManager.Stub // Being our own client is not interesting. continue; } - int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); + computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); + if (client.containsCycle) { + // We've detected a cycle. We should ignore this connection and allow + // this process to retry computeOomAdjLocked later in case a later-checked + // connection from a client would raise its priority legitimately. + app.containsCycle = true; + continue; + } + int clientAdj = client.curRawAdj; int clientProcState = client.curProcState; if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here @@ -23936,8 +23968,10 @@ public class ActivityManagerService extends IActivityManager.Stub app.curSchedGroup = schedGroup; app.curProcState = procState; app.foregroundActivities = foregroundActivities; + app.completedAdjSeq = mAdjSeq; - return app.curRawAdj; + // if curAdj is less than prevAppAdj, then this process was promoted + return app.curAdj < prevAppAdj; } /** @@ -24911,12 +24945,23 @@ public class ActivityManagerService extends IActivityManager.Stub int nextCachedAdj = curCachedAdj+1; int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; + + boolean retryCycles = false; + + // need to reset cycle state before calling computeOomAdjLocked because of service connections + for (int i=N-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + app.containsCycle = false; + } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); + // if any app encountered a cycle, we need to perform an additional loop later + retryCycles |= app.containsCycle; + // If we haven't yet assigned the final cached adj // to the process, do that now. if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { @@ -24970,6 +25015,39 @@ public class ActivityManagerService extends IActivityManager.Stub } } + + } + } + + // Cycle strategy: + // - Retry computing any process that has encountered a cycle. + // - Continue retrying until no process was promoted. + // - Iterate from least important to most important. + int cycleCount = 0; + while (retryCycles) { + cycleCount++; + retryCycles = false; + + for (int i=0; i<N; i++) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.killedByAm && app.thread != null && app.containsCycle == true) { + app.adjSeq--; + app.completedAdjSeq--; + } + } + + for (int i=0; i<N; i++) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.killedByAm && app.thread != null && app.containsCycle == true) { + if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) { + retryCycles = true; + } + } + } + } + for (int i=N-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.killedByAm && app.thread != null) { applyOomAdjLocked(app, true, now, nowElapsed); // Count the number of process types. diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index b7fde1da1eda..caf52e359548 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -149,6 +149,8 @@ final class ProcessRecord { String waitingToKill; // Process is waiting to be killed when in the bg, and reason Object forcingToImportant; // Token that is forcing this process to be important int adjSeq; // Sequence id for identifying oom_adj assignment cycles + int completedAdjSeq; // Sequence id for identifying oom_adj assignment cycles + boolean containsCycle; // Whether this app has encountered a cycle in the most recent update int lruSeq; // Sequence id for identifying LRU update cycles CompatibilityInfo compat; // last used compatibility mode IBinder.DeathRecipient deathRecipient; // Who is watching for the death. diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 02459bde09a8..36a2476d2ceb 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -26,6 +26,7 @@ import android.net.NetworkCapabilities; import android.net.wifi.WifiInfo; import android.os.UserHandle; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -131,16 +132,17 @@ public class NetworkNotificationManager { final String tag = tagFor(id); final int eventId = notifyType.eventId; final int transportType; - final String extraInfo; + final String name; if (nai != null) { transportType = getFirstTransportType(nai); - extraInfo = nai.networkInfo.getExtraInfo(); + final String extraInfo = nai.networkInfo.getExtraInfo(); + name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo; // Only notify for Internet-capable networks. if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return; } else { // Legacy notifications. transportType = TRANSPORT_CELLULAR; - extraInfo = null; + name = null; } // Clear any previous notification with lower priority, otherwise return. http://b/63676954. @@ -157,9 +159,8 @@ public class NetworkNotificationManager { if (DBG) { Slog.d(TAG, String.format( - "showNotification tag=%s event=%s transport=%s extraInfo=%s highPrioriy=%s", - tag, nameOf(eventId), getTransportName(transportType), extraInfo, - highPriority)); + "showNotification tag=%s event=%s transport=%s name=%s highPriority=%s", + tag, nameOf(eventId), getTransportName(transportType), name, highPriority)); } Resources r = Resources.getSystem(); @@ -188,7 +189,7 @@ public class NetworkNotificationManager { break; default: title = r.getString(R.string.network_available_sign_in, 0); - details = r.getString(R.string.network_available_sign_in_detailed, extraInfo); + details = r.getString(R.string.network_available_sign_in_detailed, name); break; } } else if (notifyType == NotificationType.NETWORK_SWITCH) { diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 03d8f395e36e..d7057f45e135 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -166,7 +166,7 @@ public final class ContentService extends IContentService.Stub { private SyncManager getSyncManager() { synchronized(mSyncManagerLock) { try { - // Try to create the SyncManager, return null if it fails (e.g. the disk is full). + // Try to create the SyncManager, return null if it fails (which it shouldn't). if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); } catch (SQLiteException e) { Log.e(TAG, "Can't create SyncManager", e); @@ -199,7 +199,7 @@ public final class ContentService extends IContentService.Stub { final long identityToken = clearCallingIdentity(); try { if (mSyncManager == null) { - pw.println("No SyncManager created! (Disk full?)"); + pw.println("SyncManager not available yet"); } else { mSyncManager.dump(fd, pw, dumpAll); } diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java index 9980930d87c8..089632dbe01c 100644 --- a/services/core/java/com/android/server/content/SyncJobService.java +++ b/services/core/java/com/android/server/content/SyncJobService.java @@ -115,7 +115,10 @@ public class SyncJobService extends JobService { Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: " + params.getStopReason()); } - mLogger.log("onStopJob() ", mLogger.jobParametersToString(params)); + final boolean readyToSync = SyncManager.readyToSync(); + + mLogger.log("onStopJob() ", mLogger.jobParametersToString(params), + " readyToSync=", readyToSync); synchronized (mLock) { final int jobId = params.getJobId(); mJobParamsMap.remove(jobId); @@ -124,13 +127,15 @@ public class SyncJobService extends JobService { final long nowUptime = SystemClock.uptimeMillis(); final long runtime = nowUptime - startUptime; + if (startUptime == 0) { wtf("Job " + jobId + " start uptime not found: " + " params=" + jobParametersToString(params)); } else if (runtime > 60 * 1000) { // WTF if startSyncH() hasn't happened, *unless* onStopJob() was called too soon. // (1 minute threshold.) - if (!mStartedSyncs.get(jobId)) { + // Also don't wtf when it's not ready to sync. + if (readyToSync && !mStartedSyncs.get(jobId)) { wtf("Job " + jobId + " didn't start: " + " startUptime=" + startUptime + " nowUptime=" + nowUptime diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 5fa42457ac2f..33cf11b86650 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -91,6 +91,7 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -215,6 +216,10 @@ public class SyncManager { private static final int SYNC_ADAPTER_CONNECTION_FLAGS = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT; + /** Singleton instance. */ + @GuardedBy("SyncManager.class") + private static SyncManager sInstance; + private Context mContext; private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; @@ -571,6 +576,14 @@ public class SyncManager { } public SyncManager(Context context, boolean factoryTest) { + synchronized (SyncManager.class) { + if (sInstance == null) { + sInstance = this; + } else { + Slog.wtf(TAG, "SyncManager instantiated multiple times"); + } + } + // Initialize the SyncStorageEngine first, before registering observers // and creating threads and so on; it may fail if the disk is full. mContext = context; @@ -2851,6 +2864,16 @@ public class SyncManager { } /** + * @return whether the device is ready to run sync jobs. + */ + public static boolean readyToSync() { + synchronized (SyncManager.class) { + return sInstance != null && sInstance.mProvisioned && sInstance.mBootCompleted + && sInstance.mJobServiceReady; + } + } + + /** * Handles SyncOperation Messages that are posted to the associated * HandlerThread. */ diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 030c91532135..213ec364f78f 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -189,7 +189,7 @@ public final class ColorDisplayService extends SystemService mController = new ColorDisplayController(getContext(), mCurrentUser); mController.setListener(this); - setCoefficientMatrix(getContext(), DisplayTransformManager.isNativeModeEnabled()); + setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix()); // Prepare color transformation matrix. setMatrix(mController.getColorTemperature(), mMatrixNight); @@ -293,7 +293,7 @@ public final class ColorDisplayService extends SystemService mColorMatrixAnimator.cancel(); } - setCoefficientMatrix(getContext(), DisplayTransformManager.isColorModeNative(mode)); + setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode)); setMatrix(mController.getColorTemperature(), mMatrixNight); final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); @@ -306,13 +306,12 @@ public final class ColorDisplayService extends SystemService } /** - * Set coefficients based on native mode. Use DisplayTransformManager#isNativeModeEnabled while - * setting is stable; when setting is changing, pass native mode selection directly. + * Set coefficients based on whether the color matrix is linear or not. */ - private void setCoefficientMatrix(Context context, boolean isNative) { - final String[] coefficients = context.getResources().getStringArray(isNative - ? R.array.config_nightDisplayColorTemperatureCoefficientsNative - : R.array.config_nightDisplayColorTemperatureCoefficients); + private void setCoefficientMatrix(Context context, boolean needsLinear) { + final String[] coefficients = context.getResources().getStringArray(needsLinear + ? R.array.config_nightDisplayColorTemperatureCoefficients + : R.array.config_nightDisplayColorTemperatureCoefficientsNative); for (int i = 0; i < 9 && i < coefficients.length; i++) { mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]); } diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java index 57d88e190720..57a4f0dd526d 100644 --- a/services/core/java/com/android/server/display/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/DisplayTransformManager.java @@ -231,21 +231,19 @@ public class DisplayTransformManager { } /** - * Return true when colors are stretched from the working color space to the - * native color space. + * Return true when the color matrix works in linear space. */ - public static boolean isNativeModeEnabled() { + public static boolean needsLinearColorMatrix() { return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR, - DISPLAY_COLOR_MANAGED) != DISPLAY_COLOR_MANAGED; + DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED; } /** - * Return true when the specified colorMode stretches colors from the - * working color space to the native color space. + * Return true when the specified colorMode requires the color matrix to + * work in linear space. */ - public static boolean isColorModeNative(int colorMode) { - return !(colorMode == ColorDisplayController.COLOR_MODE_NATURAL || - colorMode == ColorDisplayController.COLOR_MODE_BOOSTED); + public static boolean needsLinearColorMatrix(int colorMode) { + return colorMode != ColorDisplayController.COLOR_MODE_SATURATED; } public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) { diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 0d1644bb399f..b3aa0bf255e1 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -45,6 +45,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; @@ -82,6 +83,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final SessionStub mSession; private final SessionCb mSessionCb; private final MediaSessionService mService; + private final Context mContext; private final Object mLock = new Object(); private final ArrayList<ISessionControllerCallbackHolder> mControllerCallbackHolders = @@ -126,8 +128,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mSession = new SessionStub(); mSessionCb = new SessionCb(cb); mService = service; + mContext = mService.getContext(); mHandler = new MessageHandler(handlerLooper); - mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(); } @@ -232,12 +235,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @param packageName The package that made the original volume request. * @param pid The pid that made the original volume request. * @param uid The uid that made the original volume request. + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. This helps sessions to distinguish + * between the key injection by the app and key events from the hardware devices. + * Should be used only when the volume key events aren't handled by foreground + * activity. {@code false} otherwise to tell session about the real caller. * @param direction The direction to adjust volume in. * @param flags Any of the flags from {@link AudioManager}. * @param useSuggested True to use adjustSuggestedStreamVolume instead of */ - public void adjustVolume(String packageName, int pid, int uid, int direction, int flags, - boolean useSuggested) { + public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, + int direction, int flags, boolean useSuggested) { int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND; if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { flags &= ~AudioManager.FLAG_PLAY_SOUND; @@ -258,7 +266,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { Log.w(TAG, "Muting remote playback is not supported"); return; } - mSessionCb.adjustVolume(packageName, pid, uid, direction); + mSessionCb.adjustVolume(packageName, pid, uid, asSystemService, direction); int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); mOptimisticVolume = volumeBefore + direction; @@ -418,9 +426,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return mSessionCb.mCb; } - public void sendMediaButton(String packageName, int pid, int uid, KeyEvent ke, int sequenceId, - ResultReceiver cb) { - mSessionCb.sendMediaButton(packageName, pid, uid, ke, sequenceId, cb); + public void sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + KeyEvent ke, int sequenceId, ResultReceiver cb) { + mSessionCb.sendMediaButton(packageName, pid, uid, asSystemService, ke, sequenceId, cb); } public void dump(PrintWriter pw, String prefix) { @@ -698,11 +706,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } private String getPackageName(int uid) { - Context context = mService.getContext(); - if (context == null) { - return null; - } - String[] packages = context.getPackageManager().getPackagesForUid(uid); + String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages != null && packages.length > 0) { return packages[0]; } @@ -907,12 +911,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mCb = cb; } - public boolean sendMediaButton(String packageName, int pid, int uid, KeyEvent keyEvent, - int sequenceId, ResultReceiver cb) { + public boolean sendMediaButton(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, int sequenceId, ResultReceiver cb) { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); try { - mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb); + if (asSystemService) { + mCb.onMediaButton(mContext.getPackageName(), Process.myPid(), + Process.SYSTEM_UID, mediaButtonIntent, sequenceId, cb); + } else { + mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb); + } return true; } catch (RemoteException e) { Slog.e(TAG, "Remote failure in sendMediaRequest.", e); @@ -1079,9 +1088,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } - public void adjustVolume(String packageName, int pid, int uid, int direction) { + public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService, + int direction) { try { - mCb.onAdjustVolume(packageName, pid, uid, direction); + if (asSystemService) { + mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(), + Process.SYSTEM_UID, direction); + } else { + mCb.onAdjustVolume(packageName, pid, uid, direction); + } } catch (RemoteException e) { Slog.e(TAG, "Remote failure in adjustVolume.", e); } @@ -1105,9 +1120,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public boolean sendMediaButton(String packageName, KeyEvent mediaButtonIntent) { + public boolean sendMediaButton(String packageName, boolean asSystemService, + KeyEvent mediaButtonIntent) { return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(), - Binder.getCallingUid(), mediaButtonIntent, 0, null); + Binder.getCallingUid(), asSystemService, mediaButtonIntent, 0, null); } @Override @@ -1188,13 +1204,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void adjustVolume(String packageName, int direction, int flags) { + public void adjustVolume(String packageName, boolean asSystemService, int direction, + int flags) { int pid = Binder.getCallingPid(); int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { - MediaSessionRecord.this.adjustVolume(packageName, pid, uid, direction, flags, - false /* useSuggested */); + MediaSessionRecord.this.adjustVolume(packageName, pid, uid, asSystemService, + direction, flags, false /* useSuggested */); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index a3c6c80c75aa..a6e9389b3549 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1093,13 +1093,21 @@ public class MediaSessionService extends SystemService implements Monitor { * registered listeners, or if there was none, broadcast an * ACTION_MEDIA_BUTTON intent to the rest of the system. * + * @param packageName The caller package + * @param asSystemService {@code true} if the event sent to the session as if it was come + * from the system service instead of the app process. This helps sessions to + * distinguish between the key injection by the app and key events from the + * hardware devices. Should be used only when the volume key events aren't handled + * by foreground activity. {@code false} otherwise to tell session about the real + * caller. * @param keyEvent a non-null KeyEvent whose key code is one of the * supported media buttons * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held * while this key event is dispatched. */ @Override - public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { + public void dispatchMediaKeyEvent(String packageName, boolean asSystemService, + KeyEvent keyEvent, boolean needWakeLock) { if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) { Log.w(TAG, "Attempted to dispatch null or non-media key event."); return; @@ -1110,7 +1118,8 @@ public class MediaSessionService extends SystemService implements Monitor { final long token = Binder.clearCallingIdentity(); try { if (DEBUG) { - Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event=" + Log.d(TAG, "dispatchMediaKeyEvent, pkg=" + packageName + " pid=" + pid + + ", uid=" + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); } if (!isUserSetupComplete()) { @@ -1137,7 +1146,8 @@ public class MediaSessionService extends SystemService implements Monitor { } try { mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, - new MediaKeyListenerResultReceiver(keyEvent, needWakeLock)); + new MediaKeyListenerResultReceiver(packageName, pid, uid, + asSystemService, keyEvent, needWakeLock)); return; } catch (RemoteException e) { Log.w(TAG, "Failed to send " + keyEvent @@ -1146,9 +1156,11 @@ public class MediaSessionService extends SystemService implements Monitor { } } if (!isGlobalPriorityActive && isVoiceKey(keyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(keyEvent, needWakeLock); + handleVoiceKeyEventLocked(packageName, pid, uid, asSystemService, keyEvent, + needWakeLock); } else { - dispatchMediaKeyEventLocked(keyEvent, needWakeLock); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); } } } finally { @@ -1324,6 +1336,13 @@ public class MediaSessionService extends SystemService implements Monitor { * there's no active global priority session, long-pressess will be sent to the * long-press listener instead of adjusting volume. * + * @param packageName The caller package. + * @param asSystemService {@code true} if the event sent to the session as if it was come + * from the system service instead of the app process. This helps sessions to + * distinguish between the key injection by the app and key events from the + * hardware devices. Should be used only when the volume key events aren't handled + * by foreground activity. {@code false} otherwise to tell session about the real + * caller. * @param keyEvent a non-null KeyEvent whose key code is one of the * {@link KeyEvent#KEYCODE_VOLUME_UP}, * {@link KeyEvent#KEYCODE_VOLUME_DOWN}, @@ -1332,7 +1351,8 @@ public class MediaSessionService extends SystemService implements Monitor { * @param musicOnly true if both UI nor haptic feedback aren't needed when adjust volume. */ @Override - public void dispatchVolumeKeyEvent(KeyEvent keyEvent, int stream, boolean musicOnly) { + public void dispatchVolumeKeyEvent(String packageName, boolean asSystemService, + KeyEvent keyEvent, int stream, boolean musicOnly) { if (keyEvent == null || (keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_UP && keyEvent.getKeyCode() != KeyEvent.KEYCODE_VOLUME_DOWN @@ -1346,15 +1366,16 @@ public class MediaSessionService extends SystemService implements Monitor { final long token = Binder.clearCallingIdentity(); if (DEBUG_KEY_EVENT) { - Log.d(TAG, "dispatchVolumeKeyEvent, pid=" + pid + ", uid=" + uid + ", event=" - + keyEvent); + Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid=" + + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); } try { synchronized (mLock) { if (isGlobalPriorityActiveLocked() || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { - dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); + dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, stream, musicOnly); } else { // TODO: Consider the case when both volume up and down keys are pressed // at the same time. @@ -1387,11 +1408,12 @@ public class MediaSessionService extends SystemService implements Monitor { && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent .getDownTime() == keyEvent.getDownTime()) { // Short-press. Should change volume. - dispatchVolumeKeyEventLocked( + dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, mCurrentFullUserRecord.mInitialDownVolumeStream, mCurrentFullUserRecord.mInitialDownMusicOnly); - dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); + dispatchVolumeKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, stream, musicOnly); } else { dispatchVolumeKeyLongPressLocked(keyEvent); } @@ -1403,8 +1425,8 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private void dispatchVolumeKeyEventLocked( - KeyEvent keyEvent, int stream, boolean musicOnly) { + private void dispatchVolumeKeyEventLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, int stream, boolean musicOnly) { boolean down = keyEvent.getAction() == KeyEvent.ACTION_DOWN; boolean up = keyEvent.getAction() == KeyEvent.ACTION_UP; int direction = 0; @@ -1438,21 +1460,27 @@ public class MediaSessionService extends SystemService implements Monitor { if (up) { direction = 0; } - dispatchAdjustVolumeLocked(stream, direction, flags); + dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream, + direction, flags); } else if (isMute) { if (down && keyEvent.getRepeatCount() == 0) { - dispatchAdjustVolumeLocked(stream, AudioManager.ADJUST_TOGGLE_MUTE, flags); + dispatchAdjustVolumeLocked(packageName, pid, uid, asSystemService, stream, + AudioManager.ADJUST_TOGGLE_MUTE, flags); } } } } @Override - public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) { + public void dispatchAdjustVolume(String packageName, int suggestedStream, int delta, + int flags) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - dispatchAdjustVolumeLocked(suggestedStream, delta, flags); + dispatchAdjustVolumeLocked(packageName, pid, uid, false, + suggestedStream, delta, flags); } } finally { Binder.restoreCallingIdentity(token); @@ -1777,7 +1805,8 @@ public class MediaSessionService extends SystemService implements Monitor { return false; } - private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) { + private void dispatchAdjustVolumeLocked(String packageName, int pid, int uid, + boolean asSystemService, int suggestedStream, int direction, int flags) { MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); @@ -1822,12 +1851,13 @@ public class MediaSessionService extends SystemService implements Monitor { } }); } else { - session.adjustVolume(getContext().getPackageName(), Process.myPid(), - Process.SYSTEM_UID, direction, flags, true); + session.adjustVolume(packageName, pid, uid, asSystemService, + direction, flags, true); } } - private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { + private void handleVoiceKeyEventLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { int action = keyEvent.getAction(); boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; if (action == KeyEvent.ACTION_DOWN) { @@ -1844,14 +1874,17 @@ public class MediaSessionService extends SystemService implements Monitor { if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { // Resend the down then send this event through KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN); - dispatchMediaKeyEventLocked(downEvent, needWakeLock); - dispatchMediaKeyEventLocked(keyEvent, needWakeLock); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + downEvent, needWakeLock); + dispatchMediaKeyEventLocked(packageName, pid, uid, asSystemService, + keyEvent, needWakeLock); } } } } - private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { + private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_KEY_EVENT) { @@ -1861,17 +1894,13 @@ public class MediaSessionService extends SystemService implements Monitor { mKeyEventReceiver.aquireWakeLockLocked(); } // If we don't need a wakelock use -1 as the id so we won't release it later. - session.sendMediaButton(getContext().getPackageName(), - Process.myPid(), - Process.SYSTEM_UID, - keyEvent, + session.sendMediaButton(packageName, pid, uid, asSystemService, keyEvent, needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver); if (mCurrentFullUserRecord.mCallback != null) { try { mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( - keyEvent, - new MediaSession.Token(session.getControllerBinder())); + keyEvent, new MediaSession.Token(session.getControllerBinder())); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } @@ -1884,6 +1913,10 @@ public class MediaSessionService extends SystemService implements Monitor { Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + // TODO: Find a way to also send PID/UID in secure way. + String callerPackageName = + (asSystemService) ? getContext().getPackageName() : packageName; + mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName); try { if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; @@ -1984,13 +2017,22 @@ public class MediaSessionService extends SystemService implements Monitor { } private class MediaKeyListenerResultReceiver extends ResultReceiver implements Runnable { - private KeyEvent mKeyEvent; - private boolean mNeedWakeLock; + private final String mPackageName; + private final int mPid; + private final int mUid; + private final boolean mAsSystemService; + private final KeyEvent mKeyEvent; + private final boolean mNeedWakeLock; private boolean mHandled; - private MediaKeyListenerResultReceiver(KeyEvent keyEvent, boolean needWakeLock) { + private MediaKeyListenerResultReceiver(String packageName, int pid, int uid, + boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { super(mHandler); mHandler.postDelayed(this, MEDIA_KEY_LISTENER_TIMEOUT); + mPackageName = packageName; + mPid = pid; + mUid = uid; + mAsSystemService = asSystemService; mKeyEvent = keyEvent; mNeedWakeLock = needWakeLock; } @@ -2020,9 +2062,11 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { if (!isGlobalPriorityActiveLocked() && isVoiceKey(mKeyEvent.getKeyCode())) { - handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock); + handleVoiceKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + mKeyEvent, mNeedWakeLock); } else { - dispatchMediaKeyEventLocked(mKeyEvent, mNeedWakeLock); + dispatchMediaKeyEventLocked(mPackageName, mPid, mUid, mAsSystemService, + mKeyEvent, mNeedWakeLock); } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index a6dfec7821b6..f082271ab094 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -389,16 +389,11 @@ public final class OverlayManagerService extends SystemService { final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); if (pi != null) { - /* - * Only update overlay settings when an overlay becomes enabled or disabled. - * Enabling or disabling components of a target should not change the - * target's overlays. Since, overlays do not have components, this will only - * update overlay settings if an overlay package becomes enabled or - * disabled. - */ mPackageManager.cachePackageInfo(packageName, userId, pi); if (pi.isOverlayPackage()) { mImpl.onOverlayPackageChanged(packageName, userId); + } else { + mImpl.onTargetPackageChanged(packageName, userId); } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index bb36ab189765..36390823bd40 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -212,15 +212,21 @@ final class OverlayManagerServiceImpl { } } + void onTargetPackageChanged(@NonNull final String packageName, final int userId) { + if (DEBUG) { + Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); + } + + updateAllOverlaysForTarget(packageName, userId, 0); + } + void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) { if (DEBUG) { Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId); } - if (updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING)) { - mListener.onOverlaysChanged(packageName, userId); - } + updateAllOverlaysForTarget(packageName, userId, FLAG_TARGET_IS_UPGRADING); } void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) { @@ -228,9 +234,7 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId); } - if (updateAllOverlaysForTarget(packageName, userId, 0)) { - mListener.onOverlaysChanged(packageName, userId); - } + updateAllOverlaysForTarget(packageName, userId, 0); } void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { @@ -688,6 +692,11 @@ final class OverlayManagerServiceImpl { } interface OverlayChangeListener { + + /** + * An event triggered by changes made to overlay state or settings as well as changes that + * add or remove target packages of overlays. + **/ void onOverlaysChanged(@NonNull String targetPackage, int userId); } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index ebab1a72a227..b0be4a9799bb 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -34,6 +34,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.DexoptUtils; import com.android.server.pm.dex.PackageDexUsage; @@ -495,10 +496,9 @@ public class PackageDexOptimizer { boolean isUsedByOtherApps) { int flags = info.flags; boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0; - // When pm.dexopt.priv-apps-oob is true, we only verify privileged apps. - if (info.isPrivilegedApp() && - SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) { - return "verify"; + // When a priv app is configured to run out of box, only verify it. + if (info.isPrivilegedApp() && DexManager.isPackageSelectedToRunOob(info.packageName)) { + return "verify"; } if (vmSafeMode) { return getSafeModeCompilerFilter(targetCompilerFilter); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 00d2f74ab0a0..2650ef0b884f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -578,8 +578,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; - private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; - /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { @@ -2459,7 +2457,7 @@ public class PackageManagerService extends IPackageManager.Stub "*dexopt*"); DexManager.Listener dexManagerListener = DexLogger.getListener(this, installer, mInstallLock); - mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock, + mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener); mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock); mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); @@ -10428,11 +10426,7 @@ public class PackageManagerService extends IPackageManager.Stub Log.d(TAG, "Scanning package " + pkg.packageName); } - if (Build.IS_DEBUGGABLE && - pkg.isPrivileged() && - SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) { - PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg); - } + DexManager.maybeLogUnexpectedPackageDetails(pkg); // Initialize package source and resource directories final File scanFile = new File(pkg.codePath); @@ -21038,23 +21032,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM); co.onChange(true); - // This observer provides an one directional mapping from Global.PRIV_APP_OOB_ENABLED to - // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once - // it is done. - ContentObserver privAppOobObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0); - SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, - oobEnabled == 1 ? "true" : "false"); - } - }; - mContext.getContentResolver().registerContentObserver( - Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver, - UserHandle.USER_SYSTEM); - // At boot, restore the value from the setting, which persists across reboot. - privAppOobObserver.onChange(true); - // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, @@ -21143,6 +21120,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); storage.registerListener(mStorageListener); mInstallerService.systemReady(); + mDexManager.systemReady(); mPackageDexOptimizer.systemReady(); StorageManagerInternal StorageManagerInternal = LocalServices.getService( diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 4b907f4047b1..1aea8f0b0543 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -26,7 +26,6 @@ import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; @@ -56,7 +55,6 @@ import android.util.ArraySet; import android.util.Log; import android.util.PackageUtils; import android.util.Slog; -import android.util.jar.StrictJarFile; import android.util.proto.ProtoOutputStream; import dalvik.system.VMRuntime; @@ -85,12 +83,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; /** * Class containing helper methods for the PackageManagerService. @@ -317,61 +313,6 @@ public class PackageManagerServiceUtils { return maxModifiedTime; } - /** - * Checks that the archive located at {@code fileName} has uncompressed dex file and so - * files that can be direclty mapped. - */ - public static void logApkHasUncompressedCode(String fileName) { - StrictJarFile jarFile = null; - try { - jarFile = new StrictJarFile(fileName, - false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); - Iterator<ZipEntry> it = jarFile.iterator(); - while (it.hasNext()) { - ZipEntry entry = it.next(); - if (entry.getName().endsWith(".dex")) { - if (entry.getMethod() != ZipEntry.STORED) { - Slog.w(TAG, "APK " + fileName + " has compressed dex code " + - entry.getName()); - } else if ((entry.getDataOffset() & 0x3) != 0) { - Slog.w(TAG, "APK " + fileName + " has unaligned dex code " + - entry.getName()); - } - } else if (entry.getName().endsWith(".so")) { - if (entry.getMethod() != ZipEntry.STORED) { - Slog.w(TAG, "APK " + fileName + " has compressed native code " + - entry.getName()); - } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { - Slog.w(TAG, "APK " + fileName + " has unaligned native code " + - entry.getName()); - } - } - } - } catch (IOException ignore) { - Slog.wtf(TAG, "Error when parsing APK " + fileName); - } finally { - try { - if (jarFile != null) { - jarFile.close(); - } - } catch (IOException ignore) {} - } - return; - } - - /** - * Checks that the APKs in the given package have uncompressed dex file and so - * files that can be direclty mapped. - */ - public static void logPackageHasUncompressedCode(PackageParser.Package pkg) { - logApkHasUncompressedCode(pkg.baseCodePath); - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - for (int i = 0; i < pkg.splitCodePaths.length; i++) { - logApkHasUncompressedCode(pkg.splitCodePaths[i]); - } - } - } - private static File getSettingsProblemFile() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a92fbb672b0c..01f84c44498b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -25,6 +25,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import android.accounts.IAccountManager; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.IIntentReceiver; @@ -53,17 +54,20 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; +import android.content.pm.dex.ArtManager; import android.content.pm.dex.DexMetadataHelper; +import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; -import android.os.BaseBundle; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.IUserManager; import android.os.ParcelFileDescriptor; +import android.os.ParcelFileDescriptor.AutoCloseInputStream; +import android.os.ParcelFileDescriptor.AutoCloseOutputStream; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; @@ -78,27 +82,41 @@ import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.PrintWriterPrinter; - import com.android.internal.content.PackageHelper; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.SystemConfig; - import dalvik.system.DexFile; - -import libcore.io.IoUtils; - -import java.io.FileDescriptor; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.net.URISyntaxException; -import java.util.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.FileAttribute; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; +import libcore.io.IoUtils; +import libcore.io.Streams; class PackageManagerShellCommand extends ShellCommand { /** Path for streaming APK content */ private static final String STDIN_PATH = "-"; + /** Path where ART profiles snapshots are dumped for the shell user */ + private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/"; final IPackageManager mInterface; final private WeakHashMap<String, Resources> mResourceCache = @@ -167,6 +185,8 @@ class PackageManagerShellCommand extends ShellCommand { return runDexoptJob(); case "dump-profiles": return runDumpProfiles(); + case "snapshot-profile": + return runSnapshotProfile(); case "uninstall": return runUninstall(); case "clear": @@ -1287,6 +1307,120 @@ class PackageManagerShellCommand extends ShellCommand { return 0; } + private int runSnapshotProfile() throws RemoteException { + PrintWriter pw = getOutPrintWriter(); + + // Parse the arguments + final String packageName = getNextArg(); + final boolean isBootImage = "android".equals(packageName); + + String codePath = null; + String opt; + while ((opt = getNextArg()) != null) { + switch (opt) { + case "--code-path": + if (isBootImage) { + pw.write("--code-path cannot be used for the boot image."); + return -1; + } + codePath = getNextArg(); + break; + default: + pw.write("Unknown arg: " + opt); + return -1; + } + } + + // If no code path was explicitly requested, select the base code path. + String baseCodePath = null; + if (!isBootImage) { + PackageInfo packageInfo = mInterface.getPackageInfo(packageName, /* flags */ 0, + /* userId */0); + if (packageInfo == null) { + pw.write("Package not found " + packageName); + return -1; + } + baseCodePath = packageInfo.applicationInfo.getBaseCodePath(); + if (codePath == null) { + codePath = baseCodePath; + } + } + + // Create the profile snapshot. + final SnapshotRuntimeProfileCallback callback = new SnapshotRuntimeProfileCallback(); + // The calling package is needed to debug permission access. + final String callingPackage = (Binder.getCallingUid() == Process.ROOT_UID) + ? "root" : "com.android.shell"; + final int profileType = isBootImage + ? ArtManager.PROFILE_BOOT_IMAGE : ArtManager.PROFILE_APPS; + if (!mInterface.getArtManager().isRuntimeProfilingEnabled(profileType, callingPackage)) { + pw.println("Error: Runtime profiling is not enabled"); + return -1; + } + mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName, + codePath, callback, callingPackage); + if (!callback.waitTillDone()) { + pw.println("Error: callback not called"); + return callback.mErrCode; + } + + // Copy the snapshot profile to the output profile file. + try (InputStream inStream = new AutoCloseInputStream(callback.mProfileReadFd)) { + final String outputFileSuffix = isBootImage || Objects.equals(baseCodePath, codePath) + ? "" : ("-" + new File(codePath).getName()); + final String outputProfilePath = + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + packageName + outputFileSuffix + ".prof"; + try (OutputStream outStream = new FileOutputStream(outputProfilePath)) { + Streams.copy(inStream, outStream); + } + } catch (IOException e) { + pw.println("Error when reading the profile fd: " + e.getMessage()); + e.printStackTrace(pw); + return -1; + } + return 0; + } + + private static class SnapshotRuntimeProfileCallback + extends ISnapshotRuntimeProfileCallback.Stub { + private boolean mSuccess = false; + private int mErrCode = -1; + private ParcelFileDescriptor mProfileReadFd = null; + private CountDownLatch mDoneSignal = new CountDownLatch(1); + + @Override + public void onSuccess(ParcelFileDescriptor profileReadFd) { + mSuccess = true; + try { + // We need to dup the descriptor. We are in the same process as system server + // and we will be receiving the same object (which will be closed on the + // server side). + mProfileReadFd = profileReadFd.dup(); + } catch (IOException e) { + e.printStackTrace(); + } + mDoneSignal.countDown(); + } + + @Override + public void onError(int errCode) { + mSuccess = false; + mErrCode = errCode; + mDoneSignal.countDown(); + } + + boolean waitTillDone() { + boolean done = false; + try { + // The time-out is an arbitrary large value. Since this is a local call the result + // will come very fast. + done = mDoneSignal.await(10000000, TimeUnit.MILLISECONDS); + } catch (InterruptedException ignored) { + } + return done && mSuccess; + } + } + private int runUninstall() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); int flags = 0; @@ -2761,7 +2895,13 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(""); pw.println(" dump-profiles TARGET-PACKAGE"); pw.println(" Dumps method/class profile files to"); - pw.println(" /data/misc/profman/TARGET-PACKAGE.txt"); + pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + "TARGET-PACKAGE.txt"); + pw.println(""); + pw.println(" snapshot-profile TARGET-PACKAGE [--code-path path]"); + pw.println(" Take a snapshot of the package profiles to"); + pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + + "TARGET-PACKAGE[-code-path].prof"); + pw.println(" If TARGET-PACKAGE=android it will take a snapshot of the boot image"); pw.println(""); pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT"); pw.println(" Set the default home activity (aka launcher)."); diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 1d5c5808d705..0ba78226a38f 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -34,6 +34,7 @@ import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; @@ -44,6 +45,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; +import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; @@ -106,7 +108,7 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl()); } - private boolean checkPermission(int callingUid, String callingPackage) { + private boolean checkAndroidPermissions(int callingUid, String callingPackage) { // Callers always need this permission mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_RUNTIME_PROFILES, TAG); @@ -125,11 +127,51 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } } + /** + * Checks if the calling user is the shell user and if it is, it checks if it can + * to take a profile snapshot of the give package: + * - on debuggable builds the shell user can take profile snapshots of any app. + * - on non-debuggable builds the shell user can only take snapshots of debuggable apps. + * + * Returns true iff the callingUid is the shell uid and the shell is allowed snapshot profiles. + * + * Note that the root users will go through the regular {@link #checkAndroidPermissions) checks. + */ + private boolean checkShellPermissions(@ProfileType int profileType, String packageName, + int callingUid) { + if (callingUid != Process.SHELL_UID) { + return false; + } + if (RoSystemProperties.DEBUGGABLE) { + return true; + } + if (profileType == ArtManager.PROFILE_BOOT_IMAGE) { + // The shell cannot profile the boot image on non-debuggable builds. + return false; + } + PackageInfo info = null; + try { + info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0); + } catch (RemoteException ignored) { + // Should not happen. + } + if (info == null) { + return false; + } + + // On user builds the shell can only profile debuggable apps. + return (info.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) + == ApplicationInfo.FLAG_DEBUGGABLE; + } + + @Override public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback, String callingPackage) { - if (!checkPermission(Binder.getCallingUid(), callingPackage)) { + int callingUid = Binder.getCallingUid(); + if (!checkShellPermissions(profileType, packageName, callingUid) && + !checkAndroidPermissions(callingUid, callingPackage)) { try { callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); } catch (RemoteException ignored) { @@ -266,7 +308,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { @Override public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) { - if (!checkPermission(Binder.getCallingUid(), callingPackage)) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SHELL_UID && !checkAndroidPermissions(callingUid, callingPackage)) { return false; } diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 3e63fb42f0ef..392d4d839c45 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -16,17 +16,25 @@ package com.android.server.pm.dex; +import android.content.ContentResolver; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.database.ContentObserver; +import android.os.Build; import android.os.FileUtils; import android.os.RemoteException; import android.os.storage.StorageManager; +import android.os.SystemProperties; import android.os.UserHandle; - +import android.provider.Settings.Global; import android.util.Slog; +import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageDexOptimizer; @@ -36,13 +44,16 @@ import com.android.server.pm.PackageManagerServiceCompilerMapping; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.zip.ZipEntry; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; @@ -59,8 +70,14 @@ import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; public class DexManager { private static final String TAG = "DexManager"; + private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; + private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST = + "pm.dexopt.priv-apps-oob-list"; + private static final boolean DEBUG = false; + private final Context mContext; + // Maps package name to code locations. // It caches the code locations for the installed packages. This allows for // faster lookups (no locks) when finding what package owns the dex file. @@ -106,8 +123,9 @@ public class DexManager { String dexPath, int storageFlags); } - public DexManager(IPackageManager pms, PackageDexOptimizer pdo, + public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo, Installer installer, Object installLock, Listener listener) { + mContext = context; mPackageCodeLocationsCache = new HashMap<>(); mPackageDexUsage = new PackageDexUsage(); mPackageManager = pms; @@ -117,6 +135,10 @@ public class DexManager { mListener = listener; } + public void systemReady() { + registerSettingObserver(); + } + /** * Notify about dex files loads. * Note that this method is invoked when apps load dex files and it should @@ -641,6 +663,141 @@ public class DexManager { mPackageDexUsage.writeNow(); } + private void registerSettingObserver() { + final ContentResolver resolver = mContext.getContentResolver(); + + // This observer provides a one directional mapping from Global.PRIV_APP_OOB_ENABLED to + // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once + // it is done. + ContentObserver privAppOobObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0); + SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, + oobEnabled == 1 ? "true" : "false"); + } + }; + resolver.registerContentObserver( + Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver, + UserHandle.USER_SYSTEM); + // At boot, restore the value from the setting, which persists across reboot. + privAppOobObserver.onChange(true); + + ContentObserver privAppOobListObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + String oobList = Global.getString(resolver, Global.PRIV_APP_OOB_LIST); + if (oobList == null) { + oobList = "ALL"; + } + SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, oobList); + } + }; + resolver.registerContentObserver( + Global.getUriFor(Global.PRIV_APP_OOB_LIST), false, privAppOobListObserver, + UserHandle.USER_SYSTEM); + // At boot, restore the value from the setting, which persists across reboot. + privAppOobListObserver.onChange(true); + } + + /** + * Returns whether the given package is in the list of privilaged apps that should run out of + * box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that when + * the the OOB list is empty, all priv apps will run in OOB mode. + */ + public static boolean isPackageSelectedToRunOob(String packageName) { + return isPackageSelectedToRunOob(Arrays.asList(packageName)); + } + + /** + * Returns whether any of the given packages are in the list of privilaged apps that should run + * out of box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that + * when the the OOB list is empty, all priv apps will run in OOB mode. + */ + public static boolean isPackageSelectedToRunOob(Collection<String> packageNamesInSameProcess) { + if (!SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) { + return false; + } + String oobListProperty = SystemProperties.get( + PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, "ALL"); + if ("ALL".equals(oobListProperty)) { + return true; + } + for (String oobPkgName : oobListProperty.split(",")) { + if (packageNamesInSameProcess.contains(oobPkgName)) { + return true; + } + } + return false; + } + + /** + * Generates package related log if the package has code stored in unexpected way. + */ + public static void maybeLogUnexpectedPackageDetails(PackageParser.Package pkg) { + if (!Build.IS_DEBUGGABLE) { + return; + } + + if (pkg.isPrivileged() && isPackageSelectedToRunOob(pkg.packageName)) { + logIfPackageHasUncompressedCode(pkg); + } + } + + /** + * Generates log if the APKs in the given package have uncompressed dex file and so + * files that can be direclty mapped. + */ + private static void logIfPackageHasUncompressedCode(PackageParser.Package pkg) { + logIfApkHasUncompressedCode(pkg.baseCodePath); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (int i = 0; i < pkg.splitCodePaths.length; i++) { + logIfApkHasUncompressedCode(pkg.splitCodePaths[i]); + } + } + } + + /** + * Generates log if the archive located at {@code fileName} has uncompressed dex file and so + * files that can be direclty mapped. + */ + private static void logIfApkHasUncompressedCode(String fileName) { + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(fileName, + false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); + Iterator<ZipEntry> it = jarFile.iterator(); + while (it.hasNext()) { + ZipEntry entry = it.next(); + if (entry.getName().endsWith(".dex")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.w(TAG, "APK " + fileName + " has compressed dex code " + + entry.getName()); + } else if ((entry.getDataOffset() & 0x3) != 0) { + Slog.w(TAG, "APK " + fileName + " has unaligned dex code " + + entry.getName()); + } + } else if (entry.getName().endsWith(".so")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.w(TAG, "APK " + fileName + " has compressed native code " + + entry.getName()); + } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { + Slog.w(TAG, "APK " + fileName + " has unaligned native code " + + entry.getName()); + } + } + } + } catch (IOException ignore) { + Slog.wtf(TAG, "Error when parsing APK " + fileName); + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) {} + } + } + public static class RegisterDexModuleResult { public RegisterDexModuleResult() { this(false, null); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index d3526f761301..36fc1202006f 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -449,9 +449,10 @@ public class PermissionManagerService { userId) == PackageManager.PERMISSION_GRANTED) { EventLog.writeEvent(0x534e4554, "72710897", newPackage.applicationInfo.uid, - "Revoking permission", permissionName, "from package", - packageName, "as the group changed from", - oldPermissionGroupName, "to", newPermissionGroupName); + "Revoking permission " + permissionName + + " from package " + packageName + + " as the group changed from " + oldPermissionGroupName + + " to " + newPermissionGroupName); try { revokeRuntimePermission(permissionName, packageName, false, @@ -620,9 +621,8 @@ public class PermissionManagerService { enforcePermissionCapLocked(info, tree); bp = new BasePermission(info.name, tree.getSourcePackageName(), BasePermission.TYPE_DYNAMIC); - } else if (bp.isDynamic()) { - // TODO: switch this back to SecurityException - Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + } else if (!bp.isDynamic()) { + throw new SecurityException("Not allowed to modify non-dynamic permission " + info.name); } changed = bp.addToTree(fixedLevel, info, tree); diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java index b196dec93686..884d7d497a8f 100644 --- a/services/core/java/com/android/server/policy/IconUtilities.java +++ b/services/core/java/com/android/server/policy/IconUtilities.java @@ -16,6 +16,8 @@ package com.android.server.policy; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; @@ -38,24 +40,17 @@ import android.content.Context; * Various utilities shared amongst the Launcher's classes. */ public final class IconUtilities { - private static final String TAG = "IconUtilities"; - - private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; private int mIconWidth = -1; private int mIconHeight = -1; private int mIconTextureWidth = -1; private int mIconTextureHeight = -1; - private final Paint mPaint = new Paint(); - private final Paint mBlurPaint = new Paint(); - private final Paint mGlowColorPressedPaint = new Paint(); - private final Paint mGlowColorFocusedPaint = new Paint(); private final Rect mOldBounds = new Rect(); private final Canvas mCanvas = new Canvas(); private final DisplayMetrics mDisplayMetrics; - private int mColorIndex = 0; + private ColorFilter mDisabledColorFilter; public IconUtilities(Context context) { final Resources resources = context.getResources(); @@ -65,39 +60,10 @@ public final class IconUtilities { mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size); mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2); - - mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL)); - - TypedValue value = new TypedValue(); - mGlowColorPressedPaint.setColor(context.getTheme().resolveAttribute( - android.R.attr.colorPressedHighlight, value, true) ? value.data : 0xffffc300); - mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30)); - mGlowColorFocusedPaint.setColor(context.getTheme().resolveAttribute( - android.R.attr.colorFocusedHighlight, value, true) ? value.data : 0xffff8e00); - mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30)); - - ColorMatrix cm = new ColorMatrix(); - cm.setSaturation(0.2f); - mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); } - public Drawable createIconDrawable(Drawable src) { - Bitmap scaled = createIconBitmap(src); - - StateListDrawable result = new StateListDrawable(); - - result.addState(new int[] { android.R.attr.state_focused }, - new BitmapDrawable(createSelectedBitmap(scaled, false))); - result.addState(new int[] { android.R.attr.state_pressed }, - new BitmapDrawable(createSelectedBitmap(scaled, true))); - result.addState(new int[0], new BitmapDrawable(scaled)); - - result.setBounds(0, 0, mIconTextureWidth, mIconTextureHeight); - return result; - } - /** * Returns a bitmap suitable for the all apps view. The bitmap will be a power * of two sized ARGB_8888 bitmap that can be used as a gl texture. @@ -150,15 +116,6 @@ public final class IconUtilities { final int left = (textureWidth-width) / 2; final int top = (textureHeight-height) / 2; - if (false) { - // draw a big box for the icon for debugging - canvas.drawColor(sColors[mColorIndex]); - if (++mColorIndex >= sColors.length) mColorIndex = 0; - Paint debugPaint = new Paint(); - debugPaint.setColor(0xffcccc00); - canvas.drawRect(left, top, left+width, top+height, debugPaint); - } - mOldBounds.set(icon.getBounds()); icon.setBounds(left, top, left+width, top+height); icon.draw(canvas); @@ -167,24 +124,28 @@ public final class IconUtilities { return bitmap; } - private Bitmap createSelectedBitmap(Bitmap src, boolean pressed) { - final Bitmap result = Bitmap.createBitmap(mIconTextureWidth, mIconTextureHeight, - Bitmap.Config.ARGB_8888); - final Canvas dest = new Canvas(result); - - dest.drawColor(0, PorterDuff.Mode.CLEAR); - - int[] xy = new int[2]; - Bitmap mask = src.extractAlpha(mBlurPaint, xy); - - dest.drawBitmap(mask, xy[0], xy[1], - pressed ? mGlowColorPressedPaint : mGlowColorFocusedPaint); - - mask.recycle(); - - dest.drawBitmap(src, 0, 0, mPaint); - dest.setBitmap(null); - - return result; + public ColorFilter getDisabledColorFilter() { + if (mDisabledColorFilter != null) { + return mDisabledColorFilter; + } + ColorMatrix brightnessMatrix = new ColorMatrix(); + float brightnessF = 0.5f; + int brightnessI = (int) (255 * brightnessF); + // Brightness: C-new = C-old*(1-amount) + amount + float scale = 1f - brightnessF; + float[] mat = brightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = brightnessI; + mat[9] = brightnessI; + mat[14] = brightnessI; + + ColorMatrix filterMatrix = new ColorMatrix(); + filterMatrix.setSaturation(0); + filterMatrix.preConcat(brightnessMatrix); + + mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix); + return mDisabledColorFilter; } } diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java index cfce5cfdd801..c04c1fb4f786 100644 --- a/services/core/java/com/android/server/power/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java @@ -310,6 +310,11 @@ public class BatterySaverPolicy extends ContentObserver { return R.string.config_batterySaverDeviceSpecificConfig; } + @VisibleForTesting + boolean isAccessibilityEnabled() { + return mAccessibilityManager.isEnabled(); + } + @Override public void onChange(boolean selfChange, Uri uri) { refreshSettings(); @@ -403,7 +408,7 @@ public class BatterySaverPolicy extends ContentObserver { parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "")).toSysFileMap(); // Update the effective policy. - mAccessibilityEnabled = mAccessibilityManager.isEnabled(); + mAccessibilityEnabled = isAccessibilityEnabled(); mVibrationDisabledEffective = mVibrationDisabledConfig && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on. diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 966ca4104710..ee03aff1fa09 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; @@ -31,9 +32,12 @@ 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_DOCK_TASK_FROM_RECENTS; +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.AppTransition.isKeyguardGoingAwayTransit; 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; @@ -1684,12 +1688,24 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + private boolean shouldAnimate(int transit) { + final boolean isSplitScreenPrimary = + getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN; + + // We animate always if it's not split screen primary, and only some special cases in split + // screen primary because it causes issues with stack clipping when we run an un-minimize + // animation at the same time. + return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation; + } + boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { - if (mService.mDisableTransitionAnimation) { + if (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) { if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { - Slog.v(TAG_WM, "applyAnimation: transition animation is disabled. atoken=" + this); + Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped." + + " atoken=" + this); } cancelAnimation(); return false; diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 553b4fe53115..9da69170654c 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -426,9 +426,13 @@ public class RecentsAnimationController implements DeathRecipient { removeAnimation(taskAdapter); } + // Clear any pending failsafe runnables + mService.mH.removeCallbacks(mFailsafeRunnable); + // Clear references to the runner unlinkToDeathOfRunner(); mRunner = null; + mCanceled = true; // Clear associated input consumers mService.mInputMonitor.updateInputWindowsLw(true /*force*/); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ab93a8a91fb9..729dba5377ed 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5525,9 +5525,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // If there is a profile owner, redirect to that; otherwise query the device owner. ComponentName aliasChooser = getProfileOwner(caller.getIdentifier()); if (aliasChooser == null && caller.isSystem()) { - ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); - if (deviceOwnerAdmin != null) { - aliasChooser = deviceOwnerAdmin.info.getComponent(); + synchronized (getLockObject()) { + final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); + if (deviceOwnerAdmin != null) { + aliasChooser = deviceOwnerAdmin.info.getComponent(); + } } } if (aliasChooser == null) { @@ -5906,35 +5908,41 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void forceWipeDeviceNoLock(boolean wipeExtRequested, String reason, boolean wipeEuicc) { wtfIfInLock(); - - if (wipeExtRequested) { - StorageManager sm = (StorageManager) mContext.getSystemService( - Context.STORAGE_SERVICE); - sm.wipeAdoptableDisks(); - } + boolean success = false; try { + if (wipeExtRequested) { + StorageManager sm = (StorageManager) mContext.getSystemService( + Context.STORAGE_SERVICE); + sm.wipeAdoptableDisks(); + } mInjector.recoverySystemRebootWipeUserData( - /*shutdown=*/ false, reason, /*force=*/ true, /*wipeEuicc=*/ wipeEuicc); + /*shutdown=*/ false, reason, /*force=*/ true, /*wipeEuicc=*/ wipeEuicc); + success = true; } catch (IOException | SecurityException e) { Slog.w(LOG_TAG, "Failed requesting data wipe", e); + } finally { + if (!success) SecurityLog.writeEvent(SecurityLog.TAG_WIPE_FAILURE); } } private void forceWipeUser(int userId, String wipeReasonForUser) { + boolean success = false; try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userId) { am.switchUser(UserHandle.USER_SYSTEM); } - boolean userRemoved = mUserManagerInternal.removeUserEvenWhenDisallowed(userId); - if (!userRemoved) { + success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId); + if (!success) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); } else if (isManagedProfile(userId)) { sendWipeProfileNotification(wipeReasonForUser); } } catch (RemoteException re) { // Shouldn't happen + } finally { + if (!success) SecurityLog.writeEvent(SecurityLog.TAG_WIPE_FAILURE); } } diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 2bf6e92f26a8..a7209a076461 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -429,9 +429,13 @@ public class ApfFilter { try { mHardwareAddress = mInterfaceParams.macAddr.toByteArray(); synchronized(this) { - // Clear APF memory. - byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize]; - mIpClientCallback.installPacketFilter(zeroes); + // Clear the APF memory to reset all counters upon connecting to the first AP + // in an SSID. This is limited to APFv4 devices because this large write triggers + // a crash on some older devices (b/78905546). + if (mApfCapabilities.hasDataAccess()) { + byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize]; + mIpClientCallback.installPacketFilter(zeroes); + } // Install basic filters installNewProgramLocked(); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java index a14b950a05ff..b4b34c507942 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java @@ -40,6 +40,7 @@ import android.os.UserManager; import android.support.test.filters.SmallTest; import android.testing.DexmakerShareClassLoaderRule; +import com.android.internal.app.SuspendedAppActivity; import com.android.internal.app.UnlaunchableAppActivity; import com.android.server.LocalServices; import com.android.server.pm.PackageManagerService; @@ -150,6 +151,28 @@ public class ActivityStartInterceptorTest { } @Test + public void testSuspendedPackage() { + mAInfo.applicationInfo.flags = FLAG_SUSPENDED; + final String suspendingPackage = "com.test.suspending.package"; + final String dialogMessage = "Test Message"; + when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID)) + .thenReturn(suspendingPackage); + when(mPackageManagerInternal.getSuspendedDialogMessage(TEST_PACKAGE_NAME, TEST_USER_ID)) + .thenReturn(dialogMessage); + // THEN calling intercept returns true + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null)); + + // Check intent parameters + assertEquals(dialogMessage, + mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE)); + assertEquals(suspendingPackage, + mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE)); + assertEquals(TEST_PACKAGE_NAME, + mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDED_PACKAGE)); + assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000)); + } + + @Test public void testInterceptQuietProfile() { // GIVEN that the user the activity is starting as is currently in quiet mode when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 36d0c8bec09e..147347d47444 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -111,7 +111,8 @@ public class DexManagerTests { DELEGATE_LAST_CLASS_LOADER_NAME); mDexManager = new DexManager( - mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener); + /*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, + mListener); // Foo and Bar are available to user0. // Only Bar is available to user1; diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java index 20cf733a860b..761c1f1ea3a5 100644 --- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java @@ -15,6 +15,7 @@ */ package com.android.server.power; +import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerSaveState; import android.os.Handler; @@ -41,7 +42,8 @@ public class BatterySaverPolicyTest extends AndroidTestCase { private static final float DEFAULT_BRIGHTNESS_FACTOR = 0.5f; private static final float PRECISION = 0.001f; private static final int GPS_MODE = 0; - private static final int DEFAULT_GPS_MODE = 0; + private static final int DEFAULT_GPS_MODE = + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; private static final String BATTERY_SAVER_CONSTANTS = "vibration_disabled=true," + "animation_disabled=false," + "soundtrigger_disabled=true," @@ -69,6 +71,10 @@ public class BatterySaverPolicyTest extends AndroidTestCase { return mDeviceSpecificConfigResId; } + @Override + boolean isAccessibilityEnabled() { + return mMockAccessibilityEnabled; + } @VisibleForTesting void onChange() { @@ -83,11 +89,15 @@ public class BatterySaverPolicyTest extends AndroidTestCase { private final ArrayMap<String, String> mMockGlobalSettings = new ArrayMap<>(); private int mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1; + private boolean mMockAccessibilityEnabled; + public void setUp() throws Exception { super.setUp(); MockitoAnnotations.initMocks(this); mBatterySaverPolicy = new BatterySaverPolicyForTest(mHandler); mBatterySaverPolicy.systemReady(getContext()); + + mMockAccessibilityEnabled = false; } @SmallTest @@ -101,6 +111,12 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } @SmallTest + public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() { + mMockAccessibilityEnabled = true; + testServiceDefaultValue_unchanged(ServiceType.VIBRATION); + } + + @SmallTest public void testGetBatterySaverPolicy_PolicySound_DefaultValueCorrect() { testServiceDefaultValue(ServiceType.SOUND); } @@ -117,7 +133,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { @SmallTest public void testGetBatterySaverPolicy_PolicyAnimation_DefaultValueCorrect() { - testServiceDefaultValue(ServiceType.ANIMATION); + testServiceDefaultValue_unchanged(ServiceType.ANIMATION); } @SmallTest @@ -144,11 +160,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { @SmallTest public void testGetBatterySaverPolicy_PolicyScreenBrightness_DefaultValueCorrect() { - testServiceDefaultValue(ServiceType.SCREEN_BRIGHTNESS); - - PowerSaveState stateOn = - mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, true); - assertThat(stateOn.brightnessFactor).isWithin(PRECISION).of(DEFAULT_BRIGHTNESS_FACTOR); + testServiceDefaultValue_unchanged(ServiceType.SCREEN_BRIGHTNESS); } @SmallTest @@ -222,6 +234,17 @@ public class BatterySaverPolicyTest extends AndroidTestCase { assertThat(batterySaverStateOff.batterySaverEnabled).isFalse(); } + private void testServiceDefaultValue_unchanged(@ServiceType int type) { + mBatterySaverPolicy.updateConstantsLocked("", ""); + final PowerSaveState batterySaverStateOn = + mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_ON); + assertThat(batterySaverStateOn.batterySaverEnabled).isFalse(); + + final PowerSaveState batterySaverStateOff = + mBatterySaverPolicy.getBatterySaverPolicy(type, BATTERY_SAVER_OFF); + assertThat(batterySaverStateOff.batterySaverEnabled).isFalse(); + } + public void testDeviceSpecific() { mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1; mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, ""); diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java index fbf6691f5bd2..a2af9b80fe36 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -19,6 +19,9 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; @@ -82,6 +85,25 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { verifyNoMoreInteractionsExceptAsBinder(mMockRunner); } + @Test + public void testCancelAfterRemove_expectIgnored() throws Exception { + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(), + false /* isRecentTaskInvisible */); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + + // Remove the app window so that the animation target can not be created + appWindow.removeImmediately(); + mController.startAnimation(); + mController.cleanupAnimation(REORDER_KEEP_IN_PLACE); + try { + mController.cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "test"); + } catch (Exception e) { + fail("Unexpected failure when canceling animation after finishing it"); + } + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index f4bb32de91ae..bafb0a2fd683 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -828,7 +828,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // internalClearGlobalStateLocked() cleans up the telephony and power save listeners. private void internalClearGlobalStateLocked() { // Unregister from call state changes. - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + long token = Binder.clearCallingIdentity(); + try { + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + } finally { + Binder.restoreCallingIdentity(token); + } // Unregister from power save mode changes. if (mPowerSaveModeListener != null) { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index a9389bea2452..ece646ca7b2c 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1016,6 +1016,8 @@ public class SubscriptionManager { if (iSub != null) { // FIXME: This returns 1 on success, 0 on error should should we return it? iSub.addSubInfoRecord(iccId, slotIndex); + } else { + logd("[addSubscriptionInfoRecord]- ISub service is null"); } } catch (RemoteException ex) { // ignore it diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java index 00bf33ad691e..b185a26bb972 100644 --- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java +++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java @@ -435,9 +435,10 @@ public class SoundTriggerTestService extends Service { if (!file.getName().endsWith(".properties")) { continue; } - try { + + try (FileInputStream in = new FileInputStream(file)) { Properties properties = new Properties(); - properties.load(new FileInputStream(file)); + properties.load(in); createModelInfo(properties); loadedModel = true; } catch (Exception e) { diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index 026b54f5618a..280afadebc0f 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -109,23 +109,3 @@ cc_library_shared { ], static_libs: ["libstatssocket"], } - -cc_library_static { - name: "libstatssocket", - srcs: [ - "stats_event_list.c", - "statsd_writer.c", - ], - cflags: [ - "-Wall", - "-Werror", - "-DLIBLOG_LOG_TAG=1006", - "-DWRITE_TO_STATSD=1", - "-DWRITE_TO_LOGD=0", - ], - export_include_dirs: ["include"], - shared_libs: [ - "liblog", - ], -} - diff --git a/tools/stats_log_api_gen/include/stats_event_list.h b/tools/stats_log_api_gen/include/stats_event_list.h deleted file mode 100644 index c198d97c3862..000000000000 --- a/tools/stats_log_api_gen/include/stats_event_list.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_STATS_LOG_STATS_EVENT_LIST_H -#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H - -#include <log/log_event_list.h> - -#ifdef __cplusplus -extern "C" { -#endif -void reset_log_context(android_log_context ctx); -int write_to_logger(android_log_context context, log_id_t id); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -/** - * A copy of android_log_event_list class. - * - * android_log_event_list is going to be deprecated soon, so copy it here to - * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this - * code. - */ -class stats_event_list { -private: - android_log_context ctx; - int ret; - - stats_event_list(const stats_event_list&) = delete; - void operator=(const stats_event_list&) = delete; - -public: - explicit stats_event_list(int tag) : ret(0) { - ctx = create_android_logger(static_cast<uint32_t>(tag)); - } - explicit stats_event_list(log_msg& log_msg) : ret(0) { - ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t), - log_msg.entry.len - sizeof(uint32_t)); - } - ~stats_event_list() { android_log_destroy(&ctx); } - - int close() { - int retval = android_log_destroy(&ctx); - if (retval < 0) { - ret = retval; - } - return retval; - } - - /* To allow above C calls to use this class as parameter */ - operator android_log_context() const { return ctx; } - - /* return errors or transmit status */ - int status() const { return ret; } - - int begin() { - int retval = android_log_write_list_begin(ctx); - if (retval < 0) { - ret = retval; - } - return ret; - } - int end() { - int retval = android_log_write_list_end(ctx); - if (retval < 0) { - ret = retval; - } - return ret; - } - - stats_event_list& operator<<(int32_t value) { - int retval = android_log_write_int32(ctx, value); - if (retval < 0) { - ret = retval; - } - return *this; - } - - stats_event_list& operator<<(uint32_t value) { - int retval = android_log_write_int32(ctx, static_cast<int32_t>(value)); - if (retval < 0) { - ret = retval; - } - return *this; - } - - stats_event_list& operator<<(bool value) { - int retval = android_log_write_int32(ctx, value ? 1 : 0); - if (retval < 0) { - ret = retval; - } - return *this; - } - - stats_event_list& operator<<(int64_t value) { - int retval = android_log_write_int64(ctx, value); - if (retval < 0) { - ret = retval; - } - return *this; - } - - stats_event_list& operator<<(uint64_t value) { - int retval = android_log_write_int64(ctx, static_cast<int64_t>(value)); - if (retval < 0) { - ret = retval; - } - return *this; - } - - stats_event_list& operator<<(const char* value) { - int retval = android_log_write_string8(ctx, value); - if (retval < 0) { - ret = retval; - } - return *this; - } - -#if defined(_USING_LIBCXX) - stats_event_list& operator<<(const std::string& value) { - int retval = android_log_write_string8_len(ctx, value.data(), - value.length()); - if (retval < 0) { - ret = retval; - } - return *this; - } -#endif - - stats_event_list& operator<<(float value) { - int retval = android_log_write_float32(ctx, value); - if (retval < 0) { - ret = retval; - } - return *this; - } - - int write(log_id_t id = LOG_ID_EVENTS) { - /* facilitate -EBUSY retry */ - if ((ret == -EBUSY) || (ret > 0)) { - ret = 0; - } - int retval = write_to_logger(ctx, id); - /* existing errors trump transmission errors */ - if (!ret) { - ret = retval; - } - return ret; - } - - /* - * Append<Type> methods removes any integer promotion - * confusion, and adds access to string with length. - * Append methods are also added for all types for - * convenience. - */ - - bool AppendInt(int32_t value) { - int retval = android_log_write_int32(ctx, value); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - - bool AppendLong(int64_t value) { - int retval = android_log_write_int64(ctx, value); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - - bool AppendString(const char* value) { - int retval = android_log_write_string8(ctx, value); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - - bool AppendString(const char* value, size_t len) { - int retval = android_log_write_string8_len(ctx, value, len); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - -#if defined(_USING_LIBCXX) - bool AppendString(const std::string& value) { - int retval = android_log_write_string8_len(ctx, value.data(), - value.length()); - if (retval < 0) { - ret = retval; - } - return ret; - } - - bool Append(const std::string& value) { - int retval = android_log_write_string8_len(ctx, value.data(), - value.length()); - if (retval < 0) { - ret = retval; - } - return ret; - } -#endif - - bool AppendFloat(float value) { - int retval = android_log_write_float32(ctx, value); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - - template <typename Tvalue> - bool Append(Tvalue value) { - *this << value; - return ret >= 0; - } - - bool Append(const char* value, size_t len) { - int retval = android_log_write_string8_len(ctx, value, len); - if (retval < 0) { - ret = retval; - } - return ret >= 0; - } - - android_log_list_element read() { return android_log_read_next(ctx); } - android_log_list_element peek() { return android_log_peek_next(ctx); } -}; - -#endif -#endif // ANDROID_STATS_LOG_STATS_EVENT_LIST_H diff --git a/tools/stats_log_api_gen/stats_event_list.c b/tools/stats_log_api_gen/stats_event_list.c deleted file mode 100644 index 0a342a817a46..000000000000 --- a/tools/stats_log_api_gen/stats_event_list.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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. - */ - -#include "include/stats_event_list.h" - -#include <string.h> -#include "statsd_writer.h" - -#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t)) - -typedef struct { - uint32_t tag; - unsigned pos; /* Read/write position into buffer */ - unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements */ - unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* pos for list counter */ - unsigned list_nest_depth; - unsigned len; /* Length or raw buffer. */ - bool overflow; - bool list_stop; /* next call decrement list_nest_depth and issue a stop */ - enum { - kAndroidLoggerRead = 1, - kAndroidLoggerWrite = 2, - } read_write_flag; - uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD]; -} android_log_context_internal; - -extern struct android_log_transport_write statsdLoggerWrite; - -static int __write_to_statsd_init(struct iovec* vec, size_t nr); -static int (*write_to_statsd)(struct iovec* vec, - size_t nr) = __write_to_statsd_init; - -// Similar to create_android_logger(), but instead of allocation a new buffer, -// this function resets the buffer for resuse. -void reset_log_context(android_log_context ctx) { - if (!ctx) { - return; - } - android_log_context_internal* context = - (android_log_context_internal*)(ctx); - uint32_t tag = context->tag; - memset(context, 0, sizeof(android_log_context_internal)); - - context->tag = tag; - context->read_write_flag = kAndroidLoggerWrite; - size_t needed = sizeof(uint8_t) + sizeof(uint8_t); - if ((context->pos + needed) > MAX_EVENT_PAYLOAD) { - context->overflow = true; - } - /* Everything is a list */ - context->storage[context->pos + 0] = EVENT_TYPE_LIST; - context->list[0] = context->pos + 1; - context->pos += needed; -} - -int stats_write_list(android_log_context ctx) { - android_log_context_internal* context; - const char* msg; - ssize_t len; - - context = (android_log_context_internal*)(ctx); - if (!context || (kAndroidLoggerWrite != context->read_write_flag)) { - return -EBADF; - } - - if (context->list_nest_depth) { - return -EIO; - } - - /* NB: if there was overflow, then log is truncated. Nothing reported */ - context->storage[1] = context->count[0]; - len = context->len = context->pos; - msg = (const char*)context->storage; - /* it's not a list */ - if (context->count[0] <= 1) { - len -= sizeof(uint8_t) + sizeof(uint8_t); - if (len < 0) { - len = 0; - } - msg += sizeof(uint8_t) + sizeof(uint8_t); - } - - struct iovec vec[2]; - vec[0].iov_base = &context->tag; - vec[0].iov_len = sizeof(context->tag); - vec[1].iov_base = (void*)msg; - vec[1].iov_len = len; - return write_to_statsd(vec, 2); -} - -int write_to_logger(android_log_context ctx, log_id_t id) { - int retValue = 0; - - if (WRITE_TO_LOGD) { - retValue = android_log_write_list(ctx, id); - } - - if (WRITE_TO_STATSD) { - // log_event_list's cast operator is overloaded. - int ret = stats_write_list(ctx); - // In debugging phase, we may write to both logd and statsd. Prefer to - // return statsd socket write error code here. - if (ret < 0) { - retValue = ret; - } - } - - return retValue; -} - -/* log_init_lock assumed */ -static int __write_to_statsd_initialize_locked() { - if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) { - if (statsdLoggerWrite.close) { - (*statsdLoggerWrite.close)(); - return -ENODEV; - } - } - return 1; -} - -static int __write_to_stats_daemon(struct iovec* vec, size_t nr) { - int ret, save_errno; - struct timespec ts; - size_t len, i; - - for (len = i = 0; i < nr; ++i) { - len += vec[i].iov_len; - } - if (!len) { - return -EINVAL; - } - - save_errno = errno; - clock_gettime(CLOCK_REALTIME, &ts); - - ret = 0; - - ssize_t retval; - retval = (*statsdLoggerWrite.write)(&ts, vec, nr); - if (ret >= 0) { - ret = retval; - } - - errno = save_errno; - return ret; -} - -static int __write_to_statsd_init(struct iovec* vec, size_t nr) { - int ret, save_errno = errno; - - statsd_writer_init_lock(); - - if (write_to_statsd == __write_to_statsd_init) { - ret = __write_to_statsd_initialize_locked(); - if (ret < 0) { - statsd_writer_init_unlock(); - errno = save_errno; - return ret; - } - - write_to_statsd = __write_to_stats_daemon; - } - - statsd_writer_init_unlock(); - - ret = write_to_statsd(vec, nr); - errno = save_errno; - return ret; -}
\ No newline at end of file diff --git a/tools/stats_log_api_gen/statsd_writer.c b/tools/stats_log_api_gen/statsd_writer.c deleted file mode 100644 index 3e1035899102..000000000000 --- a/tools/stats_log_api_gen/statsd_writer.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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. - */ -#include "statsd_writer.h" - -#include <cutils/sockets.h> -#include <endian.h> -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <poll.h> -#include <private/android_filesystem_config.h> -#include <private/android_logger.h> -#include <stdarg.h> -#include <stdatomic.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> -#include <time.h> -#include <unistd.h> - -/* branchless on many architectures. */ -#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y)))) - -static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; - -void statsd_writer_init_lock() { - /* - * If we trigger a signal handler in the middle of locked activity and the - * signal handler logs a message, we could get into a deadlock state. - */ - pthread_mutex_lock(&log_init_lock); -} - -int statd_writer_trylock() { - return pthread_mutex_trylock(&log_init_lock); -} - -void statsd_writer_init_unlock() { - pthread_mutex_unlock(&log_init_lock); -} - -static int statsdAvailable(); -static int statsdOpen(); -static void statsdClose(); -static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr); - -struct android_log_transport_write statsdLoggerWrite = { - .name = "statsd", - .sock = -EBADF, - .available = statsdAvailable, - .open = statsdOpen, - .close = statsdClose, - .write = statsdWrite, -}; - -/* log_init_lock assumed */ -static int statsdOpen() { - int i, ret = 0; - - i = atomic_load(&statsdLoggerWrite.sock); - if (i < 0) { - int sock = TEMP_FAILURE_RETRY( - socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); - if (sock < 0) { - ret = -errno; - } else { - struct sockaddr_un un; - memset(&un, 0, sizeof(struct sockaddr_un)); - un.sun_family = AF_UNIX; - strcpy(un.sun_path, "/dev/socket/statsdw"); - - if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un, - sizeof(struct sockaddr_un))) < 0) { - ret = -errno; - switch (ret) { - case -ENOTCONN: - case -ECONNREFUSED: - case -ENOENT: - i = atomic_exchange(&statsdLoggerWrite.sock, ret); - /* FALLTHRU */ - default: - break; - } - close(sock); - } else { - ret = atomic_exchange(&statsdLoggerWrite.sock, sock); - if ((ret >= 0) && (ret != sock)) { - close(ret); - } - ret = 0; - } - } - } - - return ret; -} - -static void __statsdClose(int negative_errno) { - int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno); - if (sock >= 0) { - close(sock); - } -} - -static void statsdClose() { - __statsdClose(-EBADF); -} - -static int statsdAvailable() { - if (atomic_load(&statsdLoggerWrite.sock) < 0) { - if (access("/dev/socket/statsdw", W_OK) == 0) { - return 0; - } - return -EBADF; - } - return 1; -} - -static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) { - ssize_t ret; - int sock; - static const unsigned headerLength = 1; - struct iovec newVec[nr + headerLength]; - android_log_header_t header; - size_t i, payloadSize; - static atomic_int dropped; - - sock = atomic_load(&statsdLoggerWrite.sock); - if (sock < 0) - switch (sock) { - case -ENOTCONN: - case -ECONNREFUSED: - case -ENOENT: - break; - default: - return -EBADF; - } - /* - * struct { - * // what we provide to socket - * android_log_header_t header; - * // caller provides - * union { - * struct { - * char prio; - * char payload[]; - * } string; - * struct { - * uint32_t tag - * char payload[]; - * } binary; - * }; - * }; - */ - - header.tid = gettid(); - header.realtime.tv_sec = ts->tv_sec; - header.realtime.tv_nsec = ts->tv_nsec; - - newVec[0].iov_base = (unsigned char*)&header; - newVec[0].iov_len = sizeof(header); - - // If we dropped events before, try to tell statsd. - if (sock >= 0) { - int32_t snapshot = - atomic_exchange_explicit(&dropped, 0, memory_order_relaxed); - if (snapshot) { - android_log_event_int_t buffer; - header.id = LOG_ID_STATS; - buffer.header.tag = htole32(LIBLOG_LOG_TAG); - buffer.payload.type = EVENT_TYPE_INT; - buffer.payload.data = htole32(snapshot); - - newVec[headerLength].iov_base = &buffer; - newVec[headerLength].iov_len = sizeof(buffer); - - ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2)); - if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { - atomic_fetch_add_explicit(&dropped, snapshot, - memory_order_relaxed); - } - } - } - - header.id = LOG_ID_STATS; - - for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) { - newVec[i].iov_base = vec[i - headerLength].iov_base; - payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len; - - if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) { - newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD; - if (newVec[i].iov_len) { - ++i; - } - break; - } - } - - /* - * The write below could be lost, but will never block. - * - * ENOTCONN occurs if statsd has died. - * ENOENT occurs if statsd is not running and socket is missing. - * ECONNREFUSED occurs if we can not reconnect to statsd. - * EAGAIN occurs if statsd is overloaded. - */ - if (sock < 0) { - ret = sock; - } else { - ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i)); - if (ret < 0) { - ret = -errno; - } - } - switch (ret) { - case -ENOTCONN: - case -ECONNREFUSED: - case -ENOENT: - if (statd_writer_trylock()) { - return ret; /* in a signal handler? try again when less stressed - */ - } - __statsdClose(ret); - ret = statsdOpen(); - statsd_writer_init_unlock(); - - if (ret < 0) { - return ret; - } - - ret = TEMP_FAILURE_RETRY( - writev(atomic_load(&statsdLoggerWrite.sock), newVec, i)); - if (ret < 0) { - ret = -errno; - } - /* FALLTHRU */ - default: - break; - } - - if (ret > (ssize_t)sizeof(header)) { - ret -= sizeof(header); - } else if (ret == -EAGAIN) { - atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); - } - - return ret; -} diff --git a/tools/stats_log_api_gen/statsd_writer.h b/tools/stats_log_api_gen/statsd_writer.h deleted file mode 100644 index 1043afbd10a4..000000000000 --- a/tools/stats_log_api_gen/statsd_writer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_STATS_LOG_STATS_WRITER_H -#define ANDROID_STATS_LOG_STATS_WRITER_H - -#include <pthread.h> -#include <stdatomic.h> -#include <sys/socket.h> - -/** - * Internal lock should not be exposed. This is bad design. - * TODO: rewrite it in c++ code and encapsulate the functionality in a - * StatsdWriter class. - */ -void statsd_writer_init_lock(); -int statsd_writer_init_trylock(); -void statsd_writer_init_unlock(); - -struct android_log_transport_write { - const char* name; /* human name to describe the transport */ - atomic_int sock; - int (*available)(); /* Does not cause resources to be taken */ - int (*open)(); /* can be called multiple times, reusing current resources */ - void (*close)(); /* free up resources */ - /* write log to transport, returns number of bytes propagated, or -errno */ - int (*write)(struct timespec* ts, struct iovec* vec, size_t nr); -}; - - -#endif // ANDROID_STATS_LOG_STATS_WRITER_H |