Merge "Add new method refreshSummary(Preference)" into pi-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 0fae011..f1c6120 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -564,6 +564,11 @@
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
+ public final class NetworkCapabilities implements android.os.Parcelable {
+ method public int[] getCapabilities();
+ method public int[] getTransportTypes();
+ }
+
public class TrafficStats {
method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 0136ec1..66afa15 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -51,7 +51,7 @@
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex);
- if (mPullTagId != -1) {
+ if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
vector<shared_ptr<LogEvent>> allData;
mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
@@ -73,7 +73,9 @@
data->setElapsedTimestampNs(eventTimeNs);
onMatchedLogEventLocked(0, *data);
}
- } else { // For pushed value metric, we simply flush and reset the current bucket start.
+ } else {
+ // For pushed value metric or pulled metric where condition is not true,
+ // we simply flush and reset the current bucket start.
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
}
@@ -170,6 +172,7 @@
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse);
FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 81d8892..087a612 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -308,6 +308,57 @@
EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue);
}
+TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+ metric.set_condition(StringToId("SCREEN_ON"));
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager =
+ make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(100);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(120);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ bucketStartTimeNs, pullerManager);
+ valueProducer.setBucketSize(60 * NS_PER_SEC);
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
+
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs-100);
+ EXPECT_FALSE(valueProducer.mCondition);
+
+ valueProducer.notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1);
+ // Expect one full buckets already done and starting a partial bucket.
+ EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+ EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
+ EXPECT_FALSE(valueProducer.mCondition);
+}
+
TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetric metric;
metric.set_id(metricId);
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index cecd564..7ecfbe6 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -3208,8 +3208,6 @@
Landroid/net/NetworkCapabilities;-><init>()V
Landroid/net/NetworkCapabilities;->addCapability(I)Landroid/net/NetworkCapabilities;
Landroid/net/NetworkCapabilities;->addTransportType(I)Landroid/net/NetworkCapabilities;
-Landroid/net/NetworkCapabilities;->getCapabilities()[I
-Landroid/net/NetworkCapabilities;->getTransportTypes()[I
Landroid/net/NetworkCapabilities;->mNetworkCapabilities:J
Landroid/net/NetworkCapabilities;->mSignalStrength:I
Landroid/net/NetworkCapabilities;->removeCapability(I)Landroid/net/NetworkCapabilities;
@@ -6664,6 +6662,7 @@
Landroid/widget/RemoteViews;->mergeRemoteViews(Landroid/widget/RemoteViews;)V
Landroid/widget/RemoteViews;->mLayoutId:I
Landroid/widget/RemoteViews;->mPortrait:Landroid/widget/RemoteViews;
+Landroid/widget/RemoteViews;->setIsWidgetCollectionChild(Z)V
Landroid/widget/RemoteViews;->setRemoteAdapter(ILjava/util/ArrayList;I)V
Landroid/widget/RemoteViewsAdapter;->getRemoteViewsServiceIntent()Landroid/content/Intent;
Landroid/widget/RemoteViewsAdapter;->isDataReady()Z
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3b62bd7..7b383d4 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -979,7 +979,9 @@
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
- newBase.setAutofillClient(this);
+ if (newBase != null) {
+ newBase.setAutofillClient(this);
+ }
}
/** @hide */
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 21a3c07..fcab8c1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -156,6 +156,12 @@
public static final int UID_STATE_FOREGROUND_SERVICE = 2;
/**
+ * Last UID state in which we don't restrict what an op can do.
+ * @hide
+ */
+ public static final int UID_STATE_LAST_NON_RESTRICTED = UID_STATE_FOREGROUND_SERVICE;
+
+ /**
* Metrics about an op when its uid is in the foreground for any other reasons.
* @hide
*/
@@ -1594,11 +1600,11 @@
}
public long getLastAccessForegroundTime() {
- return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_FOREGROUND_SERVICE + 1);
+ return maxTime(mTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
}
public long getLastAccessBackgroundTime() {
- return maxTime(mTimes, UID_STATE_FOREGROUND_SERVICE + 1, _NUM_UID_STATE);
+ return maxTime(mTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
}
public long getLastTimeFor(int uidState) {
@@ -1614,11 +1620,11 @@
}
public long getLastRejectForegroundTime() {
- return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_FOREGROUND_SERVICE + 1);
+ return maxTime(mRejectTimes, UID_STATE_PERSISTENT, UID_STATE_LAST_NON_RESTRICTED + 1);
}
public long getLastRejectBackgroundTime() {
- return maxTime(mRejectTimes, UID_STATE_FOREGROUND_SERVICE + 1, _NUM_UID_STATE);
+ return maxTime(mRejectTimes, UID_STATE_LAST_NON_RESTRICTED + 1, _NUM_UID_STATE);
}
public long getLastRejectTimeFor(int uidState) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ca3257f..9db642b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -700,7 +700,7 @@
librarySearchPath, libraryPermittedPath, mBaseClassLoader,
null /* classLoaderName */);
StrictMode.setThreadPolicy(oldPolicy);
- mAppComponentFactory = createAppFactory(mApplicationInfo, mClassLoader);
+ mAppComponentFactory = AppComponentFactory.DEFAULT;
}
return;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f771cbd..526888d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6374,6 +6374,8 @@
/**
* @hide
+ * Note that we aren't actually comparing the contents of the bitmaps here, so this
+ * is only doing a cursory inspection. Bitmaps of equal size will appear the same.
*/
@Override
public boolean areNotificationsVisiblyDifferent(Style other) {
@@ -6381,7 +6383,20 @@
return true;
}
BigPictureStyle otherS = (BigPictureStyle) other;
- return !Objects.equals(getBigPicture(), otherS.getBigPicture());
+ return areBitmapsObviouslyDifferent(getBigPicture(), otherS.getBigPicture());
+ }
+
+ private static boolean areBitmapsObviouslyDifferent(Bitmap a, Bitmap b) {
+ if (a == b) {
+ return false;
+ }
+ if (a == null || b == null) {
+ return true;
+ }
+ return a.getWidth() != b.getWidth()
+ || a.getHeight() != b.getHeight()
+ || a.getConfig() != b.getConfig()
+ || a.getGenerationId() != b.getGenerationId();
}
}
@@ -6526,6 +6541,7 @@
/**
* @hide
+ * Spans are ignored when comparing text for visual difference.
*/
@Override
public boolean areNotificationsVisiblyDifferent(Style other) {
@@ -6533,7 +6549,7 @@
return true;
}
BigTextStyle newS = (BigTextStyle) other;
- return !Objects.equals(getBigText(), newS.getBigText());
+ return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
}
static void applyBigTextContentView(Builder builder,
@@ -6900,6 +6916,7 @@
/**
* @hide
+ * Spans are ignored when comparing text for visual difference.
*/
@Override
public boolean areNotificationsVisiblyDifferent(Style other) {
@@ -6910,10 +6927,7 @@
List<MessagingStyle.Message> oldMs = getMessages();
List<MessagingStyle.Message> newMs = newS.getMessages();
- if (oldMs == null) {
- oldMs = new ArrayList<>();
- }
- if (newMs == null) {
+ if (oldMs == null || newMs == null) {
newMs = new ArrayList<>();
}
@@ -6924,16 +6938,20 @@
for (int i = 0; i < n; i++) {
MessagingStyle.Message oldM = oldMs.get(i);
MessagingStyle.Message newM = newMs.get(i);
- if (!Objects.equals(oldM.getText(), newM.getText())) {
+ if (!Objects.equals(
+ String.valueOf(oldM.getText()),
+ String.valueOf(newM.getText()))) {
return true;
}
if (!Objects.equals(oldM.getDataUri(), newM.getDataUri())) {
return true;
}
- CharSequence oldSender = oldM.getSenderPerson() == null ? oldM.getSender()
- : oldM.getSenderPerson().getName();
- CharSequence newSender = newM.getSenderPerson() == null ? newM.getSender()
- : newM.getSenderPerson().getName();
+ String oldSender = String.valueOf(oldM.getSenderPerson() == null
+ ? oldM.getSender()
+ : oldM.getSenderPerson().getName());
+ String newSender = String.valueOf(newM.getSenderPerson() == null
+ ? newM.getSender()
+ : newM.getSenderPerson().getName());
if (!Objects.equals(oldSender, newSender)) {
return true;
}
@@ -7533,7 +7551,22 @@
return true;
}
InboxStyle newS = (InboxStyle) other;
- return !Objects.equals(getLines(), newS.getLines());
+
+ final ArrayList<CharSequence> myLines = getLines();
+ final ArrayList<CharSequence> newLines = newS.getLines();
+ final int n = myLines.size();
+ if (n != newLines.size()) {
+ return true;
+ }
+
+ for (int i = 0; i < n; i++) {
+ if (!Objects.equals(
+ String.valueOf(myLines.get(i)),
+ String.valueOf(newLines.get(i)))) {
+ return true;
+ }
+ }
+ return false;
}
private void handleInboxImageMargin(RemoteViews contentView, int id, boolean first,
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index 6feb38e..85fe99d 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -17,6 +17,8 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
@@ -178,17 +180,18 @@
*/
public static final class Builder {
private final String mResultKey;
+ private final ArraySet<String> mAllowedDataTypes = new ArraySet<>();
+ private final Bundle mExtras = new Bundle();
private CharSequence mLabel;
private CharSequence[] mChoices;
private int mFlags = DEFAULT_FLAGS;
- private Bundle mExtras = new Bundle();
- private final ArraySet<String> mAllowedDataTypes = new ArraySet<>();
/**
* Create a builder object for {@link RemoteInput} objects.
+ *
* @param resultKey the Bundle key that refers to this input when collected from the user
*/
- public Builder(String resultKey) {
+ public Builder(@NonNull String resultKey) {
if (resultKey == null) {
throw new IllegalArgumentException("Result key can't be null");
}
@@ -197,22 +200,30 @@
/**
* Set a label to be displayed to the user when collecting this input.
- * @param label The label to show to users when they input a response.
+ *
+ * @param label The label to show to users when they input a response
* @return this object for method chaining
*/
- public Builder setLabel(CharSequence label) {
+ @NonNull
+ public Builder setLabel(@Nullable CharSequence label) {
mLabel = Notification.safeCharSequence(label);
return this;
}
/**
* Specifies choices available to the user to satisfy this input.
+ *
+ * <p>Note: Starting in Android P, these choices will always be shown on phones if the app's
+ * target SDK is >= P. However, these choices may also be rendered on other types of devices
+ * regardless of target SDK.
+ *
* @param choices an array of pre-defined choices for users input.
* You must provide a non-null and non-empty array if
- * you disabled free form input using {@link #setAllowFreeFormInput}.
+ * you disabled free form input using {@link #setAllowFreeFormInput}
* @return this object for method chaining
*/
- public Builder setChoices(CharSequence[] choices) {
+ @NonNull
+ public Builder setChoices(@Nullable CharSequence[] choices) {
if (choices == null) {
mChoices = null;
} else {
@@ -232,11 +243,12 @@
* @param mimeType A mime type that results are allowed to come in.
* Be aware that text results (see {@link #setAllowFreeFormInput}
* are allowed by default. If you do not want text results you will have to
- * pass false to {@code setAllowFreeFormInput}.
- * @param doAllow Whether the mime type should be allowed or not.
+ * pass false to {@code setAllowFreeFormInput}
+ * @param doAllow Whether the mime type should be allowed or not
* @return this object for method chaining
*/
- public Builder setAllowDataType(String mimeType, boolean doAllow) {
+ @NonNull
+ public Builder setAllowDataType(@NonNull String mimeType, boolean doAllow) {
if (doAllow) {
mAllowedDataTypes.add(mimeType);
} else {
@@ -252,9 +264,10 @@
* If you specify {@code false}, you must either provide a non-null
* and non-empty array to {@link #setChoices}, or enable a data result
* in {@code setAllowDataType}. Otherwise an
- * {@link IllegalArgumentException} is thrown.
+ * {@link IllegalArgumentException} is thrown
* @return this object for method chaining
*/
+ @NonNull
public Builder setAllowFreeFormInput(boolean allowFreeFormTextInput) {
setFlag(mFlags, allowFreeFormTextInput);
return this;
@@ -267,7 +280,8 @@
*
* @see RemoteInput#getExtras
*/
- public Builder addExtras(Bundle extras) {
+ @NonNull
+ public Builder addExtras(@NonNull Bundle extras) {
if (extras != null) {
mExtras.putAll(extras);
}
@@ -279,6 +293,7 @@
*
* <p>The returned Bundle is shared with this Builder.
*/
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
@@ -295,6 +310,7 @@
* Combine all of the options that have been set and return a new {@link RemoteInput}
* object.
*/
+ @NonNull
public RemoteInput build() {
return new RemoteInput(
mResultKey, mLabel, mChoices, mFlags, mExtras, mAllowedDataTypes);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index aadf904..e54d3b6 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -28,6 +28,7 @@
import android.os.Parcelable;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -427,6 +428,7 @@
* @see {@link SliceItem#getSubType()}
*/
public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) {
+ Preconditions.checkNotNull(slice);
mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
slice.getHints().toArray(new String[slice.getHints().size()])));
return this;
@@ -439,6 +441,8 @@
*/
public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
@Nullable @SliceSubtype String subType) {
+ Preconditions.checkNotNull(action);
+ Preconditions.checkNotNull(s);
List<String> hints = s.getHints();
s.mSpec = null;
mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
@@ -464,6 +468,7 @@
*/
public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType,
@SliceHint List<String> hints) {
+ Preconditions.checkNotNull(icon);
mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
return this;
}
@@ -476,6 +481,7 @@
public Slice.Builder addRemoteInput(RemoteInput remoteInput,
@Nullable @SliceSubtype String subType,
@SliceHint List<String> hints) {
+ Preconditions.checkNotNull(remoteInput);
mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
subType, hints));
return this;
@@ -523,6 +529,7 @@
*/
public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType,
@SliceHint List<String> hints) {
+ Preconditions.checkNotNull(bundle);
mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
hints));
return this;
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 6c57d81..5e75841 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -130,6 +130,10 @@
/**
* @hide
*/
+ public static final String METHOD_GET_PERMISSIONS = "get_permissions";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
@@ -147,6 +151,10 @@
* @hide
*/
public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_RESULT = "result";
private static final boolean DEBUG = false;
@@ -392,6 +400,13 @@
b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
new ArrayList<>(handleGetDescendants(uri)));
return b;
+ } else if (method.equals(METHOD_GET_PERMISSIONS)) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only the system can get permissions");
+ }
+ Bundle b = new Bundle();
+ b.putStringArray(EXTRA_RESULT, mAutoGrantPermissions);
+ return b;
}
return super.call(method, arg, extras);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 1b05402..1b07784 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -513,7 +513,7 @@
try {
mService.setAppStandbyBucket(packageName, bucket, mContext.getUserId());
} catch (RemoteException e) {
- // Nothing to do
+ throw e.rethrowFromSystemServer();
}
}
@@ -539,8 +539,8 @@
}
return bucketMap;
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- return Collections.EMPTY_MAP;
}
/**
@@ -563,6 +563,7 @@
try {
mService.setAppStandbyBuckets(slice, mContext.getUserId());
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -596,6 +597,7 @@
mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
callbackIntent, mContext.getOpPackageName());
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -614,6 +616,7 @@
try {
mService.unregisterAppUsageObserver(observerId, mContext.getOpPackageName());
} catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -698,6 +701,7 @@
try {
mService.whitelistAppTemporarily(packageName, duration, user.getIdentifier());
} catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
}
@@ -709,6 +713,7 @@
try {
mService.onCarrierPrivilegedAppsChanged();
} catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 65dfb13..a808c64 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Parcel;
import android.os.Parcelable;
@@ -427,6 +428,7 @@
* @return an array of capability values for this instance.
* @hide
*/
+ @TestApi
public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities);
}
@@ -690,6 +692,7 @@
* @return an array of transport type values for this instance.
* @hide
*/
+ @TestApi
public @Transport int[] getTransportTypes() {
return BitUtils.unpackBits(mTransportTypes);
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 839a8bf..fd5a22a 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -78,6 +78,7 @@
private final String mId;
private final File mPath;
+ private final File mInternalPath;
private final String mDescription;
private final boolean mPrimary;
private final boolean mRemovable;
@@ -118,11 +119,12 @@
public static final int STORAGE_ID_PRIMARY = 0x00010001;
/** {@hide} */
- public StorageVolume(String id, File path, String description, boolean primary,
- boolean removable, boolean emulated, boolean allowMassStorage,
+ public StorageVolume(String id, File path, File internalPath, String description,
+ boolean primary, boolean removable, boolean emulated, boolean allowMassStorage,
long maxFileSize, UserHandle owner, String fsUuid, String state) {
mId = Preconditions.checkNotNull(id);
mPath = Preconditions.checkNotNull(path);
+ mInternalPath = Preconditions.checkNotNull(internalPath);
mDescription = Preconditions.checkNotNull(description);
mPrimary = primary;
mRemovable = removable;
@@ -137,6 +139,7 @@
private StorageVolume(Parcel in) {
mId = in.readString();
mPath = new File(in.readString());
+ mInternalPath = new File(in.readString());
mDescription = in.readString();
mPrimary = in.readInt() != 0;
mRemovable = in.readInt() != 0;
@@ -163,6 +166,16 @@
return mPath.toString();
}
+ /**
+ * Returns the path of the underlying filesystem.
+ *
+ * @return the internal path
+ * @hide
+ */
+ public String getInternalPath() {
+ return mInternalPath.toString();
+ }
+
/** {@hide} */
public File getPathFile() {
return mPath;
@@ -351,6 +364,7 @@
pw.increaseIndent();
pw.printPair("mId", mId);
pw.printPair("mPath", mPath);
+ pw.printPair("mInternalPath", mInternalPath);
pw.printPair("mDescription", mDescription);
pw.printPair("mPrimary", mPrimary);
pw.printPair("mRemovable", mRemovable);
@@ -384,6 +398,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mId);
parcel.writeString(mPath.toString());
+ parcel.writeString(mInternalPath.toString());
parcel.writeString(mDescription);
parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 5c99f6c..576ea52 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -333,6 +333,10 @@
if (userPath == null) {
userPath = new File("/dev/null");
}
+ File internalPath = getInternalPathForUser(userId);
+ if (internalPath == null) {
+ internalPath = new File("/dev/null");
+ }
String description = null;
String derivedFsUuid = fsUuid;
@@ -371,7 +375,7 @@
description = context.getString(android.R.string.unknownName);
}
- return new StorageVolume(id, userPath, description, isPrimary(), removable,
+ return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
emulated, allowMassStorage, maxFileSize, new UserHandle(userId),
derivedFsUuid, envState);
}
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index d7c267b..d3d3874 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -143,4 +143,21 @@
void stopPrinterStateTracking(in PrinterId printerId, int userId);
void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer,
int userId);
+
+ /**
+ * Check if the system will bind to print services in intant app.
+ *
+ * @param userId the Id of the user the behavior should be checked for
+ *
+ * @return {@code true} iff the system will bind to print services in instant apps.
+ */
+ boolean getBindInstantServiceAllowed(int userId);
+
+ /**
+ * Set if the system will bind to print services in intant app.
+ *
+ * @param userId the Id of the user the behavior should be changed for
+ * @param allows iff {@code true} the system will bind to print services in instant apps
+ */
+ void setBindInstantServiceAllowed(int userId, boolean allowed);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 29bbddc..27f446a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -58,6 +58,7 @@
import android.database.Cursor;
import android.database.SQLException;
import android.location.LocationManager;
+import android.media.AudioFormat;
import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.net.Uri;
@@ -4186,6 +4187,7 @@
PUBLIC_SETTINGS.add(DIM_SCREEN);
PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
+ PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
@@ -10445,7 +10447,9 @@
* The following keys are supported:
*
* <pre>
- * state_settle_time (long)
+ * top_state_settle_time (long)
+ * fg_service_state_settle_time (long)
+ * bg_state_settle_time (long)
* </pre>
*
* <p>
@@ -10921,21 +10925,13 @@
= "forced_app_standby_for_small_battery_enabled";
/**
- * Whether or not to enable the Off Body, Radios Off feature on small battery devices.
+ * Whether or not to enable the User Absent, Radios Off feature on small battery devices.
* Type: int (0 for false, 1 for true)
* Default: 0
* @hide
*/
- public static final String OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
- = "off_body_radios_off_for_small_battery_enabled";
-
- /**
- * How long after the device goes off body to disable radios, in milliseconds.
- * Type: long
- * Default: 10 minutes
- * @hide
- */
- public static final String OFF_BODY_RADIOS_OFF_DELAY_MS = "off_body_radios_off_delay_ms";
+ public static final String USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED
+ = "user_absent_radios_off_for_small_battery_enabled";
/**
* Whether or not to turn on Wifi when proxy is disconnected.
@@ -11308,15 +11304,66 @@
public static final int ENCODED_SURROUND_OUTPUT_ALWAYS = 2;
/**
+ * Surround sound formats are available according to the choice
+ * of user, even if they are not detected by the hardware. Those
+ * formats will be reported as part of the HDMI output capability.
+ * Applications are then free to use either PCM or encoded output.
+ *
+ * An example use case would be an AVR that doesn't report a surround
+ * format while the user knows the AVR does support it.
+ * @hide
+ */
+ public static final int ENCODED_SURROUND_OUTPUT_MANUAL = 3;
+
+ /**
* Set to ENCODED_SURROUND_OUTPUT_AUTO,
- * ENCODED_SURROUND_OUTPUT_NEVER or
- * ENCODED_SURROUND_OUTPUT_ALWAYS
+ * ENCODED_SURROUND_OUTPUT_NEVER,
+ * ENCODED_SURROUND_OUTPUT_ALWAYS or
+ * ENCODED_SURROUND_OUTPUT_MANUAL
* @hide
*/
public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+ new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
+
+ /**
+ * Surround sounds formats that are enabled when ENCODED_SURROUND_OUTPUT is set to
+ * ENCODED_SURROUND_OUTPUT_MANUAL. Encoded as comma separated list. Allowed values
+ * are the format constants defined in AudioFormat.java. Ex:
+ *
+ * "5,6"
+ *
+ * @hide
+ */
+ public static final String ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS =
+ "encoded_surround_output_enabled_formats";
+
+ private static final Validator ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR =
+ new Validator() {
+ @Override
+ public boolean validate(String value) {
+ try {
+ String[] surroundFormats = TextUtils.split(value, ",");
+ for (String format : surroundFormats) {
+ int audioFormat = Integer.valueOf(format);
+ boolean isSurroundFormat = false;
+ for (int sf : AudioFormat.SURROUND_SOUND_ENCODING) {
+ if (sf == audioFormat) {
+ isSurroundFormat = true;
+ break;
+ }
+ }
+ if (!isSurroundFormat) {
+ return false;
+ }
+ }
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
/**
* Persisted safe headphone volume management state by AudioService
@@ -11963,6 +12010,7 @@
CALL_AUTO_RETRY,
DOCK_AUDIO_MEDIA_ENABLED,
ENCODED_SURROUND_OUTPUT,
+ ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
LOW_POWER_MODE_TRIGGER_LEVEL,
BLUETOOTH_ON,
PRIVATE_DNS_MODE,
@@ -11999,6 +12047,8 @@
VALIDATORS.put(CALL_AUTO_RETRY, CALL_AUTO_RETRY_VALIDATOR);
VALIDATORS.put(DOCK_AUDIO_MEDIA_ENABLED, DOCK_AUDIO_MEDIA_ENABLED_VALIDATOR);
VALIDATORS.put(ENCODED_SURROUND_OUTPUT, ENCODED_SURROUND_OUTPUT_VALIDATOR);
+ VALIDATORS.put(ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
+ ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS_VALIDATOR);
VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL, LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
VALIDATORS.put(LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3807079..049d34f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15049,8 +15049,7 @@
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
- float oldAlpha = mTransformationInfo.mAlpha;
- mTransformationInfo.mAlpha = alpha;
+ setAlphaInternal(alpha);
if (onSetAlpha((int) (alpha * 255))) {
mPrivateFlags |= PFLAG_ALPHA_SET;
// subclass is handling alpha - don't optimize rendering cache invalidation
@@ -15061,10 +15060,6 @@
invalidateViewProperty(true, false);
mRenderNode.setAlpha(getFinalAlpha());
}
- // Report visibility changes, which can affect children, to accessibility
- if ((alpha == 0) ^ (oldAlpha == 0)) {
- notifySubtreeAccessibilityStateChangedIfNeeded();
- }
}
}
@@ -15081,7 +15076,7 @@
boolean setAlphaNoInvalidation(float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
- mTransformationInfo.mAlpha = alpha;
+ setAlphaInternal(alpha);
boolean subclassHandlesAlpha = onSetAlpha((int) (alpha * 255));
if (subclassHandlesAlpha) {
mPrivateFlags |= PFLAG_ALPHA_SET;
@@ -15094,6 +15089,15 @@
return false;
}
+ private void setAlphaInternal(float alpha) {
+ float oldAlpha = mTransformationInfo.mAlpha;
+ mTransformationInfo.mAlpha = alpha;
+ // Report visibility changes, which can affect children, to accessibility
+ if ((alpha == 0) ^ (oldAlpha == 0)) {
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ }
+ }
+
/**
* This property is hidden and intended only for use by the Fade transition, which
* animates it to produce a visual translucency that does not side-effect (or get
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 17687c9..a4550af 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -154,8 +154,8 @@
* Sets an id for the TextSelection object.
*/
@NonNull
- public Builder setId(@NonNull String id) {
- mId = Preconditions.checkNotNull(id);
+ public Builder setId(@Nullable String id) {
+ mId = id;
return this;
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 79576bd..d468ce3 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -50,6 +50,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
+import java.util.regex.Pattern;
/**
* A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal
@@ -59,6 +60,8 @@
public class MessagingLayout extends FrameLayout {
private static final float COLOR_SHIFT_AMOUNT = 60;
+ private static final Pattern SPECIAL_CHAR_PATTERN
+ = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]");
private static final Consumer<MessagingMessage> REMOVE_MESSAGE
= MessagingMessage::removeMessage;
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
@@ -259,18 +262,26 @@
}
public Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) {
- Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- float radius = mAvatarSize / 2.0f;
- int color = findColor(senderName, layoutColor);
- mPaint.setColor(color);
- canvas.drawCircle(radius, radius, radius, mPaint);
- boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
- mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
- mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
- int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)) ;
- canvas.drawText(symbol, radius, yPos, mTextPaint);
- return Icon.createWithBitmap(bitmap);
+ if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) ||
+ SPECIAL_CHAR_PATTERN.matcher(symbol).find()) {
+ Icon avatarIcon = Icon.createWithResource(getContext(),
+ com.android.internal.R.drawable.messaging_user);
+ avatarIcon.setTint(findColor(senderName, layoutColor));
+ return avatarIcon;
+ } else {
+ Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ float radius = mAvatarSize / 2.0f;
+ int color = findColor(senderName, layoutColor);
+ mPaint.setColor(color);
+ canvas.drawCircle(radius, radius, radius, mPaint);
+ boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f;
+ mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE);
+ mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f);
+ int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2));
+ canvas.drawText(symbol, radius, yPos, mTextPaint);
+ return Icon.createWithBitmap(bitmap);
+ }
}
private int findColor(CharSequence senderName, int layoutColor) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index adaff1f..04918ba 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -48,6 +48,15 @@
jmethodID toArray;
} gArrayListMethods;
+static jclass gBooleanClass;
+static jmethodID gBooleanCstor;
+
+static jclass gIntegerClass;
+static jmethodID gIntegerCstor;
+
+static jclass gMapClass;
+static jmethodID gMapPut;
+
static jclass gAudioHandleClass;
static jmethodID gAudioHandleCstor;
static struct {
@@ -1829,6 +1838,72 @@
return jStatus;
}
+static jint
+android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject thiz,
+ jobject jSurroundFormats, jboolean reported)
+{
+ ALOGV("getSurroundFormats");
+
+ if (jSurroundFormats == NULL) {
+ ALOGE("jSurroundFormats is NULL");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jSurroundFormats, gMapClass)) {
+ ALOGE("getSurroundFormats not a map");
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ jint jStatus;
+ unsigned int numSurroundFormats = 0;
+ audio_format_t *surroundFormats = NULL;
+ bool *surroundFormatsEnabled = NULL;
+ status_t status = AudioSystem::getSurroundFormats(
+ &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
+ jStatus = nativeToJavaStatus(status);
+ goto exit;
+ }
+ if (numSurroundFormats == 0) {
+ jStatus = (jint)AUDIO_JAVA_SUCCESS;
+ goto exit;
+ }
+ surroundFormats = (audio_format_t *)calloc(numSurroundFormats, sizeof(audio_format_t));
+ surroundFormatsEnabled = (bool *)calloc(numSurroundFormats, sizeof(bool));
+ status = AudioSystem::getSurroundFormats(
+ &numSurroundFormats, surroundFormats, surroundFormatsEnabled, reported);
+ jStatus = nativeToJavaStatus(status);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::getSurroundFormats error %d", status);
+ goto exit;
+ }
+ for (size_t i = 0; i < numSurroundFormats; i++) {
+ jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
+ audioFormatFromNative(surroundFormats[i]));
+ jobject enabled = env->NewObject(gBooleanClass, gBooleanCstor, surroundFormatsEnabled[i]);
+ env->CallObjectMethod(jSurroundFormats, gMapPut, surroundFormat, enabled);
+ env->DeleteLocalRef(surroundFormat);
+ env->DeleteLocalRef(enabled);
+ }
+
+exit:
+ free(surroundFormats);
+ free(surroundFormatsEnabled);
+ return jStatus;
+}
+
+static jint
+android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz,
+ jint audioFormat, jboolean enabled)
+{
+ status_t status = AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat),
+ (bool)enabled);
+ if (status != NO_ERROR) {
+ ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status);
+ }
+ return (jint)nativeToJavaStatus(status);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1884,6 +1959,8 @@
{"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
{"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
{"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
+ {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
+ {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
};
@@ -1903,6 +1980,18 @@
gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, "toArray", "()[Ljava/lang/Object;");
+ jclass booleanClass = FindClassOrDie(env, "java/lang/Boolean");
+ gBooleanClass = MakeGlobalRefOrDie(env, booleanClass);
+ gBooleanCstor = GetMethodIDOrDie(env, booleanClass, "<init>", "(Z)V");
+
+ jclass integerClass = FindClassOrDie(env, "java/lang/Integer");
+ gIntegerClass = MakeGlobalRefOrDie(env, integerClass);
+ gIntegerCstor = GetMethodIDOrDie(env, integerClass, "<init>", "(I)V");
+
+ jclass mapClass = FindClassOrDie(env, "java/util/Map");
+ gMapClass = MakeGlobalRefOrDie(env, mapClass);
+ gMapPut = GetMethodIDOrDie(env, mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
jclass audioHandleClass = FindClassOrDie(env, "android/media/AudioHandle");
gAudioHandleClass = MakeGlobalRefOrDie(env, audioHandleClass);
gAudioHandleCstor = GetMethodIDOrDie(env, audioHandleClass, "<init>", "(I)V");
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 0765faa..99f3034 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -600,16 +600,14 @@
}
optional Ntp ntp = 84;
- // Details about the Off Body, Radios Off feature.
- message OffBodyRadiosOff {
+ // Details about the User Absent, Radios Off feature.
+ message UserAbsentRadiosOff {
option (android.msg_privacy).dest = DEST_EXPLICIT;
- // Whether or not to enable the Off Body, Radios Off feature on small battery devices.
+ // Whether or not to enable the User Absent, Radios Off feature on small battery devices.
optional SettingProto enabled_for_small_battery = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // How long after the device goes off body to disable radios, in milliseconds.
- optional SettingProto delay_ms = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
- optional OffBodyRadiosOff off_body_radios_off = 85;
+ optional UserAbsentRadiosOff user_absent_radios_off = 85;
// Whether to disable the automatic scheduling of system updates.
optional SettingProto ota_disable_automatic_update = 86 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bc43d91..268bc9c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1285,9 +1285,6 @@
adapt to the environment. This mode may be better suited for watches. -->
<bool name="config_autoBrightnessResetAmbientLuxAfterWarmUp">true</bool>
- <!-- Period of time in which to consider light samples in milliseconds. -->
- <integer name="config_autoBrightnessAmbientLightHorizon">10000</integer>
-
<!-- Screen brightness used to dim the screen when the user activity
timeout expires. May be less than the minimum allowed brightness setting
that can be set by the user. -->
@@ -3427,4 +3424,7 @@
<!-- Whether or not swipe up gesture is enabled by default -->
<bool name="config_swipe_up_gesture_default">false</bool>
+ <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
+ <bool name="config_swipe_up_gesture_setting_available">false</bool>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index db2aa8e..6d4bbe2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1905,7 +1905,6 @@
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" />
<java-symbol type="fraction" name="config_autoBrightnessAdjustmentMaxGamma" />
- <java-symbol type="integer" name="config_autoBrightnessAmbientLightHorizon"/>
<java-symbol type="integer" name="config_autoBrightnessBrighteningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessInitialLightSensorRate"/>
@@ -3302,6 +3301,7 @@
<java-symbol type="string" name="shortcut_restore_unknown_issue" />
<java-symbol type="bool" name="config_swipe_up_gesture_default" />
+ <java-symbol type="bool" name="config_swipe_up_gesture_setting_available" />
<!-- From media projection -->
<java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ab9fc13..22aca30 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -339,8 +339,7 @@
Settings.Global.NTP_SERVER,
Settings.Global.NTP_TIMEOUT,
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
- Settings.Global.OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
- Settings.Global.OFF_BODY_RADIOS_OFF_DELAY_MS,
+ Settings.Global.USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
Settings.Global.OVERLAY_DISPLAY_DEVICES,
Settings.Global.PAC_CHANGE_DELAY,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index d0a2c98..e245425 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1074,4 +1074,58 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
+ /** @hide */
+ public static final int[] SURROUND_SOUND_ENCODING = {
+ ENCODING_AC3,
+ ENCODING_E_AC3,
+ ENCODING_DTS,
+ ENCODING_DTS_HD,
+ ENCODING_AAC_LC,
+ ENCODING_DOLBY_TRUEHD,
+ ENCODING_E_AC3_JOC,
+ };
+
+ /** @hide */
+ @IntDef(flag = false, prefix = "ENCODING", value = {
+ ENCODING_AC3,
+ ENCODING_E_AC3,
+ ENCODING_DTS,
+ ENCODING_DTS_HD,
+ ENCODING_AAC_LC,
+ ENCODING_DOLBY_TRUEHD,
+ ENCODING_E_AC3_JOC }
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SurroundSoundEncoding {}
+
+ /**
+ * @hide
+ *
+ * Return default name for a surround format. This is not an International name.
+ * It is just a default to use if an international name is not available.
+ *
+ * @param audioFormat a surround format
+ * @return short default name for the format, eg. “AC3” for ENCODING_AC3.
+ */
+ public static String toDisplayName(@SurroundSoundEncoding int audioFormat) {
+ switch (audioFormat) {
+ case ENCODING_AC3:
+ return "Dolby Digital (AC3)";
+ case ENCODING_E_AC3:
+ return "Dolby Digital Plus (E_AC3)";
+ case ENCODING_DTS:
+ return "DTS";
+ case ENCODING_DTS_HD:
+ return "DTS HD";
+ case ENCODING_AAC_LC:
+ return "AAC";
+ case ENCODING_DOLBY_TRUEHD:
+ return "Dolby TrueHD";
+ case ENCODING_E_AC3_JOC:
+ return "Dolby Atmos";
+ default:
+ return "Unknown surround sound format";
+ }
+ }
+
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 566db94..3057501 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -66,6 +66,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -5014,6 +5015,66 @@
}
}
+ /**
+ * @hide
+ * Returns all surround formats.
+ * @return a map where the key is a surround format and
+ * the value indicates the surround format is enabled or not
+ */
+ public Map<Integer, Boolean> getSurroundFormats() {
+ Map<Integer, Boolean> surroundFormats = new HashMap<>();
+ int status = AudioSystem.getSurroundFormats(surroundFormats, false);
+ if (status != AudioManager.SUCCESS) {
+ // fail and bail!
+ Log.e(TAG, "getSurroundFormats failed:" + status);
+ return new HashMap<Integer, Boolean>(); // Always return a map.
+ }
+ return surroundFormats;
+ }
+
+ /**
+ * @hide
+ * Set a certain surround format as enabled or not.
+ * @param audioFormat a surround format, the value is one of
+ * {@link AudioFormat#ENCODING_AC3}, {@link AudioFormat#ENCODING_E_AC3},
+ * {@link AudioFormat#ENCODING_DTS}, {@link AudioFormat#ENCODING_DTS_HD},
+ * {@link AudioFormat#ENCODING_AAC_LC}, {@link AudioFormat#ENCODING_DOLBY_TRUEHD},
+ * {@link AudioFormat#ENCODING_E_AC3_JOC}. Once {@link AudioFormat#ENCODING_AAC_LC} is
+ * set as enabled, {@link AudioFormat#ENCODING_AAC_LC},
+ * {@link AudioFormat#ENCODING_AAC_HE_V1}, {@link AudioFormat#ENCODING_AAC_HE_V2},
+ * {@link AudioFormat#ENCODING_AAC_ELD}, {@link AudioFormat#ENCODING_AAC_XHE} are
+ * all enabled.
+ * @param enabled the required surround format state, true for enabled, false for disabled
+ * @return true if successful, otherwise false
+ */
+ public boolean setSurroundFormatEnabled(
+ @AudioFormat.SurroundSoundEncoding int audioFormat, boolean enabled) {
+ int status = AudioSystem.setSurroundFormatEnabled(audioFormat, enabled);
+ return status == AudioManager.SUCCESS;
+ }
+
+ /**
+ * @hide
+ * Returns all surround formats that are reported by the connected HDMI device.
+ * The keys are not affected by calling setSurroundFormatEnabled(), and the values
+ * are not affected by calling setSurroundFormatEnabled() when in AUTO mode.
+ * This information can used to show the AUTO setting for SurroundSound.
+ *
+ * @return a map where the key is a surround format and
+ * the value indicates the surround format is enabled or not
+ */
+ public Map<Integer, Boolean> getReportedSurroundFormats() {
+ Map<Integer, Boolean> reportedSurroundFormats = new HashMap<>();
+ int status = AudioSystem.getSurroundFormats(reportedSurroundFormats, true);
+ if (status != AudioManager.SUCCESS) {
+ // fail and bail!
+ Log.e(TAG, "getReportedSurroundFormats failed:" + status);
+ return new HashMap<Integer, Boolean>(); // Always return a map.
+ }
+ return reportedSurroundFormats;
+ }
+
+
//---------------------------------------------------------
// Inner classes
//--------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index aaba1e3..7c893d0 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -23,6 +23,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Map;
/* IF YOU CHANGE ANY OF THE CONSTANTS IN THIS FILE, DO NOT FORGET
* TO UPDATE THE CORRESPONDING NATIVE GLUE AND AudioManager.java.
@@ -715,7 +716,8 @@
public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
- public static final int NUM_FORCE_CONFIG = 15;
+ public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
+ public static final int NUM_FORCE_CONFIG = 16;
public static final int FORCE_DEFAULT = FORCE_NONE;
public static String forceUseConfigToString(int config) {
@@ -735,6 +737,7 @@
case FORCE_HDMI_SYSTEM_AUDIO_ENFORCED: return "FORCE_HDMI_SYSTEM_AUDIO_ENFORCED";
case FORCE_ENCODED_SURROUND_NEVER: return "FORCE_ENCODED_SURROUND_NEVER";
case FORCE_ENCODED_SURROUND_ALWAYS: return "FORCE_ENCODED_SURROUND_ALWAYS";
+ case FORCE_ENCODED_SURROUND_MANUAL: return "FORCE_ENCODED_SURROUND_MANUAL";
default: return "unknown config (" + config + ")" ;
}
}
@@ -836,6 +839,11 @@
public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
+ public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats,
+ boolean reported);
+
+ public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
+
// Items shared with audio service
/**
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 7976f67..e568ef7 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -35,6 +35,9 @@
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.MediaColumns;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
@@ -560,6 +563,13 @@
return MtpConstants.RESPONSE_GENERAL_ERROR;
Path newPath = obj.getPath();
boolean success = oldPath.toFile().renameTo(newPath.toFile());
+ try {
+ Os.access(oldPath.toString(), OsConstants.F_OK);
+ Os.access(newPath.toString(), OsConstants.F_OK);
+ } catch (ErrnoException e) {
+ // Ignore. Could fail if the metadata was already updated.
+ }
+
if (!mManager.endRenameObject(obj, oldPath.getFileName().toString(), success)) {
Log.e(TAG, "Failed to end rename object");
}
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index c72b827..2625e0c 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -16,7 +16,6 @@
package android.mtp;
-import android.content.Context;
import android.os.storage.StorageVolume;
/**
@@ -36,7 +35,7 @@
public MtpStorage(StorageVolume volume, int storageId) {
mStorageId = storageId;
- mPath = volume.getPath();
+ mPath = volume.getInternalPath();
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index bdc8741..a36d88d 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -399,8 +399,8 @@
*/
public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
- MtpObject root = new MtpObject(volume.getPath(), storageId, null, true);
MtpStorage storage = new MtpStorage(volume, storageId);
+ MtpObject root = new MtpObject(storage.getPath(), storageId, null, true);
mRoots.put(storageId, root);
return storage;
}
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index 0d7f3fe..b05242d 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -104,10 +104,10 @@
mainStorageDir = createNewDir(TEMP_DIR_FILE);
secondaryStorageDir = createNewDir(TEMP_DIR_FILE);
- StorageVolume mainStorage = new StorageVolume("1", mainStorageDir, "", true, false, true,
- false, -1, UserHandle.CURRENT, "", "");
- StorageVolume secondaryStorage = new StorageVolume("2", secondaryStorageDir, "", false,
- false, true, false, -1, UserHandle.CURRENT, "", "");
+ StorageVolume mainStorage = new StorageVolume("1", mainStorageDir, mainStorageDir,
+ "", true, false, true, false, -1, UserHandle.CURRENT, "", "");
+ StorageVolume secondaryStorage = new StorageVolume("2", secondaryStorageDir,
+ secondaryStorageDir, "", false, false, true, false, -1, UserHandle.CURRENT, "", "");
objectsAdded = new ArrayList<>();
objectsRemoved = new ArrayList<>();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 83d7e16..06fbf9f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -811,6 +811,8 @@
List<ResolveInfo> resolvedActivities = getPackageManager()
.queryIntentActivities(intent, 0);
if (resolvedActivities.isEmpty()) {
+ Log.w(LOG_TAG, "Advanced options activity " + mAdvancedPrintOptionsActivity + " could "
+ + "not be found");
return;
}
diff --git a/packages/SettingsLib/res/drawable/home.xml b/packages/SettingsLib/res/drawable/home.xml
deleted file mode 100644
index 8d379b3..0000000
--- a/packages/SettingsLib/res/drawable/home.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Copyright (C) 2016 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M10.0,20.0l0.0,-6.0l4.0,0.0l0.0,6.0l5.0,0.0l0.0,-8.0l3.0,0.0L12.0,3.0 2.0,12.0l3.0,0.0l0.0,8.0z"/>
-</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_mode_edit.xml b/packages/SettingsLib/res/drawable/ic_mode_edit.xml
index c1d2378..319e7eb 100644
--- a/packages/SettingsLib/res/drawable/ic_mode_edit.xml
+++ b/packages/SettingsLib/res/drawable/ic_mode_edit.xml
@@ -1,25 +1,26 @@
<!--
- Copyright (C) 2017 The Android Open Source Project
+ 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
+ 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
+ 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.
--->
+ 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.
+ -->
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20.0dp"
- android:height="20.0dp"
+ android:width="24dp"
+ android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/colorControlNormal">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="#FFFFFF"
android:pathData="M20.41,4.94l-1.35,-1.35c-0.78,-0.78 -2.05,-0.78 -2.83,0l0,0L3,16.82V21h4.18L20.41,7.77C21.2,6.99 21.2,5.72 20.41,4.94zM6.41,19.06L5,19v-1.36l9.82,-9.82l1.41,1.41L6.41,19.06z"/>
</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml b/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml
index 8801f5d..35907cc 100644
--- a/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml
+++ b/packages/SettingsLib/res/drawable/ic_qs_night_display_on.xml
@@ -18,7 +18,10 @@
android:height="64dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
- <path
- android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z"
- android:fillColor="#FFF"/>
+ <group
+ android:translateX="-1.0">
+ <path
+ android:pathData="M13,12c0,-3.57 2.2,-6.62 5.31,-7.87 0.89,-0.36 0.75,-1.69 -0.19,-1.9 -1.1,-0.24 -2.27,-0.3 -3.48,-0.14 -4.51,0.6 -8.12,4.31 -8.59,8.83C5.43,16.93 10.12,22 16,22c0.73,0 1.43,-0.08 2.12,-0.23 0.95,-0.21 1.1,-1.53 0.2,-1.9A8.471,8.471 0,0 1,13 12z"
+ android:fillColor="#FFF"/>
+ </group>
</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index c926e1f..09fa284 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -250,6 +250,18 @@
<item>Best Effort (Adaptive Bit Rate)</item>
</string-array>
+ <!-- Summaries for Android Auto Bluetooth Audio Active Device status. [CHAR LIMIT=50]-->
+ <string-array name="bluetooth_audio_active_device_summaries" >
+ <!-- Status message when the device is not Active. -->
+ <item></item>
+ <!-- Status message when the device is Active for Media and Phone. -->
+ <item>, active</item>
+ <!-- Status message when the device is Active for Media only. -->
+ <item>, active (media)</item>
+ <!-- Status message when the device is Active for Phone only. -->
+ <item>, active (phone)</item>
+ </string-array>
+
<!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
<string-array name="select_logd_size_titles">
<item>Off</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ce4aef3..bb8fbe2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1076,6 +1076,116 @@
}
/**
+ * @return resource for android auto string that describes the connection state of this device.
+ */
+ public String getCarConnectionSummary() {
+ boolean profileConnected = false; // at least one profile is connected
+ boolean a2dpNotConnected = false; // A2DP is preferred but not connected
+ boolean hfpNotConnected = false; // HFP is preferred but not connected
+ boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
+
+ for (LocalBluetoothProfile profile : getProfiles()) {
+ int connectionStatus = getProfileConnectionState(profile);
+
+ switch (connectionStatus) {
+ case BluetoothProfile.STATE_CONNECTING:
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return mContext.getString(Utils.getConnectionStateSummary(connectionStatus));
+
+ case BluetoothProfile.STATE_CONNECTED:
+ profileConnected = true;
+ break;
+
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (profile.isProfileReady()) {
+ if ((profile instanceof A2dpProfile) ||
+ (profile instanceof A2dpSinkProfile)){
+ a2dpNotConnected = true;
+ } else if ((profile instanceof HeadsetProfile) ||
+ (profile instanceof HfpClientProfile)) {
+ hfpNotConnected = true;
+ } else if (profile instanceof HearingAidProfile) {
+ hearingAidNotConnected = true;
+ }
+ }
+ break;
+ }
+ }
+
+ String batteryLevelPercentageString = null;
+ // Android framework should only set mBatteryLevel to valid range [0-100] or
+ // BluetoothDevice.BATTERY_LEVEL_UNKNOWN, any other value should be a framework bug.
+ // Thus assume here that if value is not BluetoothDevice.BATTERY_LEVEL_UNKNOWN, it must
+ // be valid
+ final int batteryLevel = getBatteryLevel();
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ // TODO: name com.android.settingslib.bluetooth.Utils something different
+ batteryLevelPercentageString =
+ com.android.settingslib.Utils.formatPercentage(batteryLevel);
+ }
+
+ // Prepare the string for the Active Device summary
+ String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
+ R.array.bluetooth_audio_active_device_summaries);
+ String activeDeviceString = activeDeviceStringsArray[0]; // Default value: not active
+ if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+ activeDeviceString = activeDeviceStringsArray[1]; // Active for Media and Phone
+ } else {
+ if (mIsActiveDeviceA2dp) {
+ activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
+ }
+ if (mIsActiveDeviceHeadset) {
+ activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
+ }
+ }
+ if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
+ activeDeviceString = activeDeviceStringsArray[1];
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+ }
+
+ if (profileConnected) {
+ if (a2dpNotConnected && hfpNotConnected) {
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(
+ R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
+ batteryLevelPercentageString, activeDeviceString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
+ activeDeviceString);
+ }
+
+ } else if (a2dpNotConnected) {
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
+ batteryLevelPercentageString, activeDeviceString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp,
+ activeDeviceString);
+ }
+
+ } else if (hfpNotConnected) {
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
+ batteryLevelPercentageString, activeDeviceString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_headset,
+ activeDeviceString);
+ }
+ } else {
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_battery_level,
+ batteryLevelPercentageString, activeDeviceString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+ }
+ }
+ }
+
+ return getBondState() == BluetoothDevice.BOND_BONDING ?
+ mContext.getString(R.string.bluetooth_pairing) : null;
+ }
+
+ /**
* @return {@code true} if {@code cachedBluetoothDevice} is a2dp device
*/
public boolean isA2dpDevice() {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
index 508107c..143a6cf 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawable/UserIconDrawableTest.java
@@ -37,7 +37,7 @@
public void getConstantState_shouldNotBeNull() {
final Bitmap b = BitmapFactory.decodeResource(
InstrumentationRegistry.getTargetContext().getResources(),
- R.drawable.home);
+ R.drawable.ic_mode_edit);
mDrawable = new UserIconDrawable(100 /* size */).setIcon(b).bake();
assertThat(mDrawable.getConstantState()).isNotNull();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5853dca..927a94f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -159,7 +160,7 @@
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Active, 10% battery");
// Set A2DP profile to be disconnected and test connection state summary
@@ -193,7 +194,7 @@
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Active, 10% battery");
// Set HFP profile to be disconnected and test connection state summary
@@ -278,6 +279,200 @@
}
@Test
+ public void getCarConnectionSummary_singleProfileConnectDisconnect() {
+ // Test without battery level
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
+
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void getCarConnectionSummary_multipleProfileConnectDisconnect() {
+ mBatteryLevel = 10;
+
+ // Set HFP, A2DP and PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%");
+
+ // Disconnect both HFP and A2DP and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected (no phone or media), battery 10%");
+
+ // Disconnect all profiles and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void getCarConnectionSummary_singleProfileActiveDeviceA2dp() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (media)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active (media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void getCarConnectionSummary_singleProfileActiveDeviceHfp() {
+ // Test without battery level
+ // Set HFP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for HFP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (phone)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active (phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set HFP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void getCarConnectionSummary_singleProfileActiveDeviceHearingAid() {
+ // Test without battery level
+ // Set Hearing Aid profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for Hearing Aid and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set Hearing Aid profile to be disconnected and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+ mCachedDevice.onProfileStateChanged(mHearingAidProfile,
+ BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void getCarConnectionSummary_multipleProfilesActiveDevice() {
+ // Test without battery level
+ // Set A2DP and HFP profiles to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and HFP and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%, active (phone)");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%, active (media)");
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set A2DP and HFP profiles to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
+ }
+
+
+ @Test
public void testDeviceName_testAliasNameAvailable() {
when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 4530f80..e853399 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2300,6 +2300,9 @@
loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS,
R.integer.def_screen_brightness);
+ loadIntegerSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
+ com.android.internal.R.integer.config_screenBrightnessForVrSettingDefault);
+
loadBooleanSetting(stmt, Settings.System.SCREEN_BRIGHTNESS_MODE,
R.bool.def_screen_brightness_automatic_mode);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 3a2d1ce..d5efcb5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -963,14 +963,11 @@
GlobalSettingsProto.Ntp.TIMEOUT_MS);
p.end(ntpToken);
- final long obroToken = p.start(GlobalSettingsProto.OFF_BODY_RADIOS_OFF);
+ final long uaroToken = p.start(GlobalSettingsProto.USER_ABSENT_RADIOS_OFF);
dumpSetting(s, p,
- Settings.Global.OFF_BODY_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
- GlobalSettingsProto.OffBodyRadiosOff.ENABLED_FOR_SMALL_BATTERY);
- dumpSetting(s, p,
- Settings.Global.OFF_BODY_RADIOS_OFF_DELAY_MS,
- GlobalSettingsProto.OffBodyRadiosOff.DELAY_MS);
- p.end(obroToken);
+ Settings.Global.USER_ABSENT_RADIOS_OFF_FOR_SMALL_BATTERY_ENABLED,
+ GlobalSettingsProto.UserAbsentRadiosOff.ENABLED_FOR_SMALL_BATTERY);
+ p.end(uaroToken);
dumpSetting(s, p,
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors.xml b/packages/SystemUI/res/drawable/ic_invert_colors.xml
index f7a86a4..77d4918 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors.xml
@@ -19,10 +19,7 @@
android:height="48dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
- <group
- android:translateX="-0.61">
- <path
- android:pathData="M17.44,7.71L12.7,2.97a0.996,0.996 0,0 0,-1.41 0L6.56,7.71c-3,3 -3.4,7.89 -0.62,11.1 1.6,1.85 3.83,2.77 6.06,2.77s4.46,-0.92 6.06,-2.77c2.78,-3.21 2.38,-8.1 -0.62,-11.1zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"
- android:fillColor="#FFFFFFFF"/>
- </group>
+ <path
+ android:pathData="M17.44,7.71L12.7,2.97a0.996,0.996 0,0 0,-1.41 0L6.56,7.71c-3,3 -3.4,7.89 -0.62,11.1 1.6,1.85 3.83,2.77 6.06,2.77s4.46,-0.92 6.06,-2.77c2.78,-3.21 2.38,-8.1 -0.62,-11.1zM12,19.59c-1.6,0 -3.11,-0.62 -4.24,-1.76C6.62,16.69 6,15.19 6,13.59s0.62,-3.11 1.76,-4.24L12,5.1v14.49z"
+ android:fillColor="#FFFFFFFF"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
index 95345b8..30024dd 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_detail_empty.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="56dp"
- android:height="56dp"
- android:viewportWidth="25.0"
- android:viewportHeight="23.5"
+ android:width="32dp"
+ android:height="29.5dp"
+ android:viewportWidth="25.6"
+ android:viewportHeight="23.6"
android:alpha="0.14"
android:tint="?android:attr/colorForeground" >
<group
diff --git a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
index ea02ba7..a72e9b8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_wifi_disabled.xml
@@ -20,7 +20,7 @@
android:viewportHeight="23.6">
<group
android:translateX="0.8"
- android:translateY="1.1">
+ android:translateY="-1.1">
<path
android:pathData="M23.66,8.11c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3 6.44,3 2.33,5.36 0.56,6.57c-0.51,0.35 -0.61,1.06 -0.23,1.54L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L23.66,8.11z"
android:fillColor="#FFFFFFFF"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml b/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml
deleted file mode 100644
index 11a3bdd..0000000
--- a/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2017, 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.
-*/
--->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="2.5dp"
- android:insetRight="2.5dp" >
- <vector
- android:width="16dp"
- android:height="16dp"
- android:viewportWidth="16.6"
- android:viewportHeight="16.6">
- <group
- android:translateX="-0.2"
- android:translateY="-0.2">
- <path
- android:pathData="M8.5,3.72c-2.62,0 -4.78,2.16 -4.78,4.78s2.16,4.78 4.78,4.78 4.78,-2.16 4.78,-4.78 -2.07,-4.78 -4.78,-4.78zM10.09,9.56L6.91,9.56c-0.59,0 -1.06,-0.48 -1.06,-1.06s0.48,-1.06 1.06,-1.06h3.19c0.59,0 1.06,0.48 1.06,1.06s-0.48,1.06 -1.07,1.06z"
- android:fillColor="#FFF"/>
- <path
- android:pathData="M8.5,0.53C4.11,0.53 0.53,4.11 0.53,8.5s3.58,7.97 7.97,7.97 7.97,-3.58 7.97,-7.97S12.89,0.53 8.5,0.53zM8.5,15.28c-3.75,0 -6.78,-3.03 -6.78,-6.78S4.75,1.72 8.5,1.72s6.78,3.03 6.78,6.78 -3.03,6.78 -6.78,6.78z"
- android:fillColor="#FFF"/>
- </group>
- </vector>
-</inset>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index d40534e..680112c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -31,6 +31,7 @@
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:minWidth="48dp"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 75403b9..0cc3c9e 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -56,8 +56,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- <include layout="@layout/brightness_mirror" />
-
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:layout_width="match_parent"
@@ -68,6 +66,8 @@
android:layout_height="match_parent"
android:visibility="invisible" />
+ <include layout="@layout/brightness_mirror" />
+
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 99138b0..24d662b 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -67,6 +67,9 @@
<item type="id" name="transformation_start_scale_x_tag"/>
<item type="id" name="transformation_start_scale_y_tag"/>
<item type="id" name="continuous_clipping_tag"/>
+ <item type="id" name="panel_alpha_animator_tag"/>
+ <item type="id" name="panel_alpha_animator_start_tag"/>
+ <item type="id" name="panel_alpha_animator_end_tag"/>
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
index 0d8ce58..12699d5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityCompat.java
@@ -17,6 +17,10 @@
package com.android.systemui.shared.system;
import android.app.Activity;
+import android.view.View;
+import android.view.ViewHierarchyEncoder;
+
+import java.io.ByteArrayOutputStream;
public class ActivityCompat {
private final Activity mWrapped;
@@ -31,4 +35,27 @@
public void registerRemoteAnimations(RemoteAnimationDefinitionCompat definition) {
mWrapped.registerRemoteAnimations(definition.getWrapped());
}
+
+ /**
+ * @see android.view.ViewDebug#dumpv2(View, ByteArrayOutputStream)
+ */
+ public boolean encodeViewHierarchy(ByteArrayOutputStream out) {
+ View view = null;
+ if (mWrapped.getWindow() != null &&
+ mWrapped.getWindow().peekDecorView() != null &&
+ mWrapped.getWindow().peekDecorView().getViewRootImpl() != null) {
+ view = mWrapped.getWindow().peekDecorView().getViewRootImpl().getView();
+ }
+ if (view == null) {
+ return false;
+ }
+
+ final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out);
+ int[] location = view.getLocationOnScreen();
+ encoder.addProperty("window:left", location[0]);
+ encoder.addProperty("window:top", location[1]);
+ view.encode(encoder);
+ encoder.endStream();
+ return true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9f382b00..31fd47f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -750,7 +750,7 @@
private DisplayClientState mDisplayClientState = new DisplayClientState();
@VisibleForTesting
- final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ protected final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 8d32e4d..139215a0 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -554,7 +554,18 @@
return null;
}
- return mInfo.displayCutout.getBounds();
+ View rootView = getRootView();
+ Region cutoutBounds = mInfo.displayCutout.getBounds();
+
+ // Transform to window's coordinate space
+ rootView.getLocationOnScreen(mLocation);
+ cutoutBounds.translate(-mLocation[0], -mLocation[1]);
+
+ // Intersect with window's frame
+ cutoutBounds.op(rootView.getLeft(), rootView.getTop(), rootView.getRight(),
+ rootView.getBottom(), Region.Op.INTERSECT);
+
+ return cutoutBounds;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ad2efbd..a9bfa45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -179,9 +179,10 @@
mBatteryMeterView = findViewById(R.id.battery);
mBatteryMeterView.setForceShowPercent(true);
+ mBatteryMeterView.setOnClickListener(this);
mClockView = findViewById(R.id.clock);
+ mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
- mDateView.setOnClickListener(this);
}
private void updateStatusText() {
@@ -413,9 +414,12 @@
@Override
public void onClick(View v) {
- if(v == mDateView){
+ if (v == mClockView) {
Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS),0);
+ } else if (v == mBatteryMeterView) {
+ Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+ Intent.ACTION_POWER_USAGE_SUMMARY),0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index d6182c4..fb4fcd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -123,7 +123,6 @@
mDataController.setMobileDataEnabled(false);
return;
}
- mHost.collapsePanels();
String carrierName = mController.getMobileDataNetworkName();
if (TextUtils.isEmpty(carrierName)) {
carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 0a39abb..69b0f31 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -32,7 +32,10 @@
import android.annotation.StringRes;
import android.annotation.TargetApi;
import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.CornerPathEffect;
@@ -206,6 +209,7 @@
@Override
public void onViewAttachedToWindow(View view) {
if (view == mLayout) {
+ mContext.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
mLayoutAttachedToWindow = true;
if (view.getTag().equals(R.string.recents_swipe_up_onboarding)) {
mHasDismissedSwipeUpTip = false;
@@ -230,6 +234,7 @@
}
mOverviewOpenedCountSinceQuickScrubTipDismiss = 0;
}
+ mContext.unregisterReceiver(mReceiver);
}
}
};
@@ -399,7 +404,7 @@
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
0, -mNavBarHeight / 2,
- WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
flags,
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -456,4 +461,13 @@
private void setOpenedOverviewCount(int openedOverviewCount) {
Prefs.putInt(mContext, OVERVIEW_OPENED_COUNT, openedOverviewCount);
}
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ hide(false);
+ }
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index c561ae8..b010199 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -2768,6 +2768,10 @@
return mNotificationViewState;
}
+ public NotificationViewState getViewState() {
+ return mNotificationViewState;
+ }
+
@Override
public boolean isAboveShelf() {
return !isOnKeyguard()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index b40dcda..08d530b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -1199,8 +1199,13 @@
}
private void updateSingleLineView() {
if (mIsChildInGroup) {
+ boolean isNewView = mSingleLineView == null;
mSingleLineView = mHybridGroupManager.bindFromNotification(
mSingleLineView, mStatusBarNotification.getNotification());
+ if (isNewView) {
+ updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
+ mSingleLineView, mSingleLineView);
+ }
} else if (mSingleLineView != null) {
removeView(mSingleLineView);
mSingleLineView = null;
@@ -1209,8 +1214,13 @@
private void updateAmbientSingleLineView() {
if (mIsChildInGroup) {
+ boolean isNewView = mAmbientSingleLineChild == null;
mAmbientSingleLineChild = mHybridGroupManager.bindAmbientFromNotification(
mAmbientSingleLineChild, mStatusBarNotification.getNotification());
+ if (isNewView) {
+ updateViewVisibility(mVisibleType, VISIBLE_TYPE_AMBIENT_SINGLELINE,
+ mAmbientSingleLineChild, mAmbientSingleLineChild);
+ }
} else if (mAmbientSingleLineChild != null) {
removeView(mAmbientSingleLineChild);
mAmbientSingleLineChild = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7681530..f14ca71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -229,6 +229,7 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mMessagingUtil = new NotificationMessagingUtil(context);
mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
+ mGroupManager.setPendingEntries(mPendingNotifications);
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -739,6 +740,7 @@
mNotificationData.getImportance(key));
mPendingNotifications.put(key, shadeEntry);
+ mGroupManager.onPendingEntryAdded(shadeEntry);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 0cc6137..eb46fba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -25,6 +25,7 @@
import android.os.Handler;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.accessibility.AccessibilityEvent;
@@ -247,25 +248,29 @@
/** Animates in the guts view via either a fade or a circular reveal. */
private void animateOpen(
boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
- if (shouldDoCircularReveal) {
- double horz = Math.max(getWidth() - x, x);
- double vert = Math.max(getHeight() - y, y);
- float r = (float) Math.hypot(horz, vert);
- // Circular reveal originating at (x, y)
- Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimateOpenListener(onAnimationEnd));
- a.start();
+ if (isAttachedToWindow()) {
+ if (shouldDoCircularReveal) {
+ double horz = Math.max(getWidth() - x, x);
+ double vert = Math.max(getHeight() - y, y);
+ float r = (float) Math.hypot(horz, vert);
+ // Circular reveal originating at (x, y)
+ Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimateOpenListener(onAnimationEnd));
+ a.start();
+ } else {
+ // Fade in content
+ this.setAlpha(0f);
+ this.animate()
+ .alpha(1f)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+ .setInterpolator(Interpolators.ALPHA_IN)
+ .setListener(new AnimateOpenListener(onAnimationEnd))
+ .start();
+ }
} else {
- // Fade in content
- this.setAlpha(0f);
- this.animate()
- .alpha(1f)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
- .setInterpolator(Interpolators.ALPHA_IN)
- .setListener(new AnimateOpenListener(onAnimationEnd))
- .start();
+ Log.w(TAG, "Failed to animate guts open");
}
}
@@ -273,29 +278,33 @@
/** Animates out the guts view via either a fade or a circular reveal. */
@VisibleForTesting
void animateClose(int x, int y, boolean shouldDoCircularReveal) {
- if (shouldDoCircularReveal) {
- // Circular reveal originating at (x, y)
- if (x == -1 || y == -1) {
- x = (getLeft() + getRight()) / 2;
- y = (getTop() + getHeight() / 2);
+ if (isAttachedToWindow()) {
+ if (shouldDoCircularReveal) {
+ // Circular reveal originating at (x, y)
+ if (x == -1 || y == -1) {
+ x = (getLeft() + getRight()) / 2;
+ y = (getTop() + getHeight() / 2);
+ }
+ double horz = Math.max(getWidth() - x, x);
+ double vert = Math.max(getHeight() - y, y);
+ float r = (float) Math.hypot(horz, vert);
+ Animator a = ViewAnimationUtils.createCircularReveal(this,
+ x, y, r, 0);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+ a.addListener(new AnimateCloseListener(this /* view */));
+ a.start();
+ } else {
+ // Fade in the blocking helper.
+ this.animate()
+ .alpha(0f)
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .setListener(new AnimateCloseListener(this /* view */))
+ .start();
}
- double horz = Math.max(getWidth() - x, x);
- double vert = Math.max(getHeight() - y, y);
- float r = (float) Math.hypot(horz, vert);
- Animator a = ViewAnimationUtils.createCircularReveal(this,
- x, y, r, 0);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
- a.addListener(new AnimateCloseListener(this /* view */));
- a.start();
} else {
- // Fade in the blocking helper.
- this.animate()
- .alpha(0f)
- .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .setListener(new AnimateCloseListener(this /* view */))
- .start();
+ Log.w(TAG, "Failed to animate guts close");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 20ab64c..cfbb4d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -20,7 +20,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -76,8 +75,8 @@
}
public RemoteAnimationAdapter getLaunchAnimation(
- ExpandableNotificationRow sourceNotification) {
- if (mStatusBar.getBarState() != StatusBarState.SHADE) {
+ ExpandableNotificationRow sourceNotification, boolean occluded) {
+ if (mStatusBar.getBarState() != StatusBarState.SHADE || occluded) {
return null;
}
AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 85fac16..8f552e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -38,6 +38,8 @@
public class DozeParameters implements TunerService.Tunable {
private static final int MAX_DURATION = 60 * 1000;
public static final String DOZE_SENSORS_WAKE_UP_FULLY = "doze_sensors_wake_up_fully";
+ public static final boolean FORCE_NO_BLANKING =
+ SystemProperties.getBoolean("debug.force_no_blanking", false);
private static IntInOutMatcher sPickupSubtypePerformsProxMatcher;
private static DozeParameters sInstance;
@@ -181,7 +183,7 @@
* @return {@code true} if screen needs to be completely black before a power transition.
*/
public boolean getDisplayNeedsBlanking() {
- return mContext.getResources().getBoolean(
+ return !FORCE_NO_BLANKING && mContext.getResources().getBoolean(
com.android.internal.R.bool.config_displayBlanksAfterDoze);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 9ec5609..06f3c50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -36,7 +36,7 @@
/**
* Controls the appearance of heads up notifications in the icon area and the header itself.
*/
-class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
+public class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
DarkIconDispatcher.DarkReceiver {
public static final int CONTENT_FADE_DURATION = 110;
public static final int CONTENT_FADE_DELAY = 100;
@@ -92,6 +92,7 @@
panelView.setHeadsUpAppearanceController(this);
mStackScroller.addOnExpandedHeightListener(mSetExpandedHeight);
mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
+ mStackScroller.setHeadsUpAppearanceController(this);
mClockView = clockView;
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
mDarkIconDispatcher.addDarkReceiver(this);
@@ -226,10 +227,10 @@
});
}
- private void updateHeader(NotificationData.Entry entry) {
+ public void updateHeader(NotificationData.Entry entry) {
ExpandableNotificationRow row = entry.row;
float headerVisibleAmount = 1.0f;
- if (row.isPinned() || row == mTrackedChild) {
+ if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
headerVisibleAmount = mExpandFraction;
}
row.setHeaderVisibleAmount(headerVisibleAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index ca65965..55ffb3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import android.app.Notification;
+import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.support.annotation.Nullable;
import android.util.Log;
@@ -29,9 +31,11 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
/**
* A class to handle notifications and their corresponding groups.
@@ -39,12 +43,14 @@
public class NotificationGroupManager implements OnHeadsUpChangedListener {
private static final String TAG = "NotificationGroupManager";
+ private static final long HEADS_UP_TRANSFER_TIMEOUT = 300;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
private OnGroupChangeListener mListener;
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
private boolean mIsUpdatingUnchangedGroup;
+ private HashMap<String, NotificationData.Entry> mPendingNotifications;
public void setOnGroupChangeListener(OnGroupChangeListener listener) {
mListener = listener;
@@ -147,6 +153,103 @@
mListener.onGroupCreatedFromChildren(group);
}
}
+ cleanUpHeadsUpStatesOnAdd(group, false /* addIsPending */);
+ }
+
+ public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
+ String groupKey = getGroupKey(shadeEntry.notification);
+ NotificationGroup group = mGroupMap.get(groupKey);
+ if (group != null) {
+ cleanUpHeadsUpStatesOnAdd(group, true /* addIsPending */);
+ }
+ }
+
+ /**
+ * Clean up the heads up states when a new child was added.
+ * @param group The group where a view was added or will be added.
+ * @param addIsPending True if is the addition still pending or false has it already been added.
+ */
+ private void cleanUpHeadsUpStatesOnAdd(NotificationGroup group, boolean addIsPending) {
+ if (!addIsPending && group.hunSummaryOnNextAddition) {
+ if (!mHeadsUpManager.isHeadsUp(group.summary.key)) {
+ mHeadsUpManager.showNotification(group.summary);
+ }
+ group.hunSummaryOnNextAddition = false;
+ }
+ // Because notification groups are not delivered as a whole unit, it may happen that a
+ // group child gets added quite a bit after the summary got posted. Our guidance is, that
+ // apps should always post the group summary as well and we'll hide it for them if the child
+ // is the only child in a group. Because of this, we also have to transfer heads up to the
+ // child, otherwise the invisible summary would be heads-upped.
+ // This transfer to the child is not always correct in case the app has just posted another
+ // child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
+ // a scenario we would transfer the heads up to the old child and the wrong notification
+ // would be heads-upped. In oder to avoid this, we'll recover from this issue and hun the
+ // summary again instead of the old child if it's within a certain timeout.
+ if (SystemClock.elapsedRealtime() - group.lastHeadsUpTransfer < HEADS_UP_TRANSFER_TIMEOUT) {
+ if (!onlySummaryAlerts(group.summary)) {
+ return;
+ }
+ int numChildren = group.children.size();
+ NotificationData.Entry isolatedChild = getIsolatedChild(getGroupKey(
+ group.summary.notification));
+ int numPendingChildren = getPendingChildrenNotAlerting(group);
+ numChildren += numPendingChildren;
+ if (isolatedChild != null) {
+ numChildren++;
+ }
+ if (numChildren <= 1) {
+ return;
+ }
+ boolean releasedChild = false;
+ ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
+ int size = children.size();
+ for (int i = 0; i < size; i++) {
+ NotificationData.Entry entry = children.get(i);
+ if (onlySummaryAlerts(entry) && entry.row.isHeadsUp()) {
+ releasedChild = true;
+ mHeadsUpManager.releaseImmediately(entry.key);
+ }
+ }
+ if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
+ && isolatedChild.row.isHeadsUp()) {
+ releasedChild = true;
+ mHeadsUpManager.releaseImmediately(isolatedChild.key);
+ }
+ if (releasedChild && !mHeadsUpManager.isHeadsUp(group.summary.key)) {
+ boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+ if (notifyImmediately) {
+ mHeadsUpManager.showNotification(group.summary);
+ } else {
+ group.hunSummaryOnNextAddition = true;
+ }
+ group.lastHeadsUpTransfer = 0;
+ }
+ }
+ }
+
+ private int getPendingChildrenNotAlerting(NotificationGroup group) {
+ if (mPendingNotifications == null) {
+ return 0;
+ }
+ int number = 0;
+ String groupKey = getGroupKey(group.summary.notification);
+ Collection<NotificationData.Entry> values = mPendingNotifications.values();
+ for (NotificationData.Entry entry : values) {
+ if (!isGroupChild(entry.notification)) {
+ continue;
+ }
+ if (!Objects.equals(getGroupKey(entry.notification), groupKey)) {
+ continue;
+ }
+ if (group.children.containsKey(entry.key)) {
+ continue;
+ }
+ if (onlySummaryAlerts(entry)) {
+ number++;
+ }
+ }
+ return number;
}
private void onEntryBecomingChild(NotificationData.Entry entry) {
@@ -421,8 +524,16 @@
|| !entry.row.isHeadsUp()) {
return;
}
+
// The parent of a suppressed group got huned, lets hun the child!
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
+
+ if (pendingInflationsWillAddChildren(notificationGroup)) {
+ // New children will actually be added to this group, let's not transfer the heads
+ // up
+ return;
+ }
+
if (notificationGroup != null) {
Iterator<NotificationData.Entry> iterator
= notificationGroup.children.values().iterator();
@@ -438,6 +549,9 @@
if (mHeadsUpManager.isHeadsUp(child.key)) {
mHeadsUpManager.updateNotification(child, true);
} else {
+ if (onlySummaryAlerts(entry)) {
+ notificationGroup.lastHeadsUpTransfer = SystemClock.elapsedRealtime();
+ }
mHeadsUpManager.showNotification(child);
}
}
@@ -445,6 +559,35 @@
mHeadsUpManager.releaseImmediately(entry.key);
}
+ private boolean onlySummaryAlerts(NotificationData.Entry entry) {
+ return entry.notification.getNotification().getGroupAlertBehavior()
+ == Notification.GROUP_ALERT_SUMMARY;
+ }
+
+ /**
+ * Check if the pending inflations will add children to this group.
+ * @param group The group to check.
+ */
+ private boolean pendingInflationsWillAddChildren(NotificationGroup group) {
+ if (mPendingNotifications == null) {
+ return false;
+ }
+ Collection<NotificationData.Entry> values = mPendingNotifications.values();
+ String groupKey = getGroupKey(group.summary.notification);
+ for (NotificationData.Entry entry : values) {
+ if (!isGroupChild(entry.notification)) {
+ continue;
+ }
+ if (!Objects.equals(getGroupKey(entry.notification), groupKey)) {
+ continue;
+ }
+ if (!group.children.containsKey(entry.key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean shouldIsolate(StatusBarNotification sbn) {
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
@@ -477,6 +620,10 @@
}
}
+ public void setPendingEntries(HashMap<String, NotificationData.Entry> pendingNotifications) {
+ mPendingNotifications = pendingNotifications;
+ }
+
public static class NotificationGroup {
public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
public NotificationData.Entry summary;
@@ -485,6 +632,12 @@
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
+ /**
+ * The time when the last heads transfer from group to child happened, while the summary
+ * has the flags to heads up on its own.
+ */
+ public long lastHeadsUpTransfer;
+ public boolean hunSummaryOnNextAddition;
@Override
public String toString() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b475b64..a3905b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,8 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.PowerManager;
import android.util.AttributeSet;
@@ -250,6 +252,33 @@
private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private int mPanelAlpha;
+ private int mCurrentPanelAlpha;
+ private final Paint mAlphaPaint = new Paint();
+ private Runnable mPanelAlphaEndAction;
+ private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mPanelAlphaEndAction != null) {
+ mPanelAlphaEndAction.run();
+ }
+ }
+ };
+ private final AnimatableProperty PANEL_ALPHA = AnimatableProperty.from(
+ "panelAlpha",
+ NotificationPanelView::setPanelAlphaInternal,
+ NotificationPanelView::getCurrentPanelAlpha,
+ R.id.panel_alpha_animator_tag,
+ R.id.panel_alpha_animator_start_tag,
+ R.id.panel_alpha_animator_end_tag);
+ private final AnimationProperties PANEL_ALPHA_OUT_PROPERTIES = new AnimationProperties()
+ .setDuration(150)
+ .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_OUT);
+ private final AnimationProperties PANEL_ALPHA_IN_PROPERTIES = new AnimationProperties()
+ .setDuration(200)
+ .setAnimationFinishListener(mAnimatorListenerAdapter)
+ .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN);
+
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(!DEBUG);
@@ -257,6 +286,8 @@
mPowerManager = context.getSystemService(PowerManager.class);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+ mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
+ setPanelAlpha(255, false /* animate */);
}
public void setStatusBar(StatusBar bar) {
@@ -2303,6 +2334,38 @@
}
@Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mCurrentPanelAlpha != 255) {
+ canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mAlphaPaint);
+ }
+ }
+
+ public float getCurrentPanelAlpha() {
+ return mCurrentPanelAlpha;
+ }
+
+ public boolean setPanelAlpha(int alpha, boolean animate) {
+ if (mPanelAlpha != alpha) {
+ mPanelAlpha = alpha;
+ PropertyAnimator.setProperty(this, PANEL_ALPHA, alpha,
+ alpha == 255 ? PANEL_ALPHA_IN_PROPERTIES : PANEL_ALPHA_OUT_PROPERTIES, animate);
+ return true;
+ }
+ return false;
+ }
+
+ public void setPanelAlphaInternal(float alpha) {
+ mCurrentPanelAlpha = (int) alpha;
+ mAlphaPaint.setARGB(mCurrentPanelAlpha, 255, 255, 255);
+ invalidate();
+ }
+
+ public void setPanelAlphaEndAction(Runnable r) {
+ mPanelAlphaEndAction = r;
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 420c517..e9c0f5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -52,6 +52,7 @@
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
import android.telecom.TelecomManager;
+import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -89,6 +90,7 @@
import com.android.systemui.util.NotificationChannels;
import java.util.List;
+import java.util.Locale;
/**
* This class contains all of the policy about which icons are installed in the status
@@ -121,7 +123,7 @@
private final Handler mHandler = new Handler();
private final CastController mCast;
private final HotspotController mHotspot;
- private final NextAlarmController mNextAlarm;
+ private final NextAlarmController mNextAlarmController;
private final AlarmManager mAlarmManager;
private final UserInfoController mUserInfoController;
private final UserManager mUserManager;
@@ -147,6 +149,7 @@
private boolean mManagedProfileIconVisible = false;
private BluetoothController mBluetooth;
+ private AlarmManager.AlarmClockInfo mNextAlarm;
public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
mContext = context;
@@ -154,7 +157,7 @@
mCast = Dependency.get(CastController.class);
mHotspot = Dependency.get(HotspotController.class);
mBluetooth = Dependency.get(BluetoothController.class);
- mNextAlarm = Dependency.get(NextAlarmController.class);
+ mNextAlarmController = Dependency.get(NextAlarmController.class);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mUserInfoController = Dependency.get(UserInfoController.class);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -242,7 +245,7 @@
mZenController.addCallback(this);
mCast.addCallback(mCastCallback);
mHotspot.addCallback(mHotspotCallback);
- mNextAlarm.addCallback(mNextAlarmCallback);
+ mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
mLocationController.addCallback(this);
@@ -270,7 +273,7 @@
mZenController.removeCallback(this);
mCast.removeCallback(mCastCallback);
mHotspot.removeCallback(mHotspotCallback);
- mNextAlarm.removeCallback(mNextAlarmCallback);
+ mNextAlarmController.removeCallback(mNextAlarmCallback);
mDataSaver.removeCallback(this);
mKeyguardMonitor.removeCallback(this);
mLocationController.removeCallback(this);
@@ -313,10 +316,29 @@
int zen = mZenController.getZen();
final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
- : R.drawable.stat_sys_alarm, null);
+ : R.drawable.stat_sys_alarm, buildAlarmContentDescription());
mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
}
+ private String buildAlarmContentDescription() {
+ if (mNextAlarm == null) {
+ return mContext.getString(R.string.status_bar_alarm);
+ }
+ return formatNextAlarm(mNextAlarm, mContext);
+ }
+
+ private static String formatNextAlarm(AlarmManager.AlarmClockInfo info, Context context) {
+ if (info == null) {
+ return "";
+ }
+ String skeleton = DateFormat.is24HourFormat(
+ context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma";
+ String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ String dateString = DateFormat.format(pattern, info.getTriggerTime()).toString();
+
+ return context.getString(R.string.accessibility_quick_settings_alarm, dateString);
+ }
+
private final void updateSimState(Intent intent) {
String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
@@ -356,8 +378,7 @@
if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
zenVisible = zen != Global.ZEN_MODE_OFF;
- zenIconId = zen == Global.ZEN_MODE_NO_INTERRUPTIONS
- ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd;
+ zenIconId = R.drawable.stat_sys_dnd;
zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
} else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
zenVisible = true;
@@ -688,6 +709,7 @@
new NextAlarmController.NextAlarmChangeCallback() {
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
+ mNextAlarm = nextAlarm;
updateAlarm();
}
};
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 65cb56c..a323e08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2081,7 +2081,7 @@
* @param animate should the change of the icons be animated.
*/
private void updateHideIconsForBouncer(boolean animate) {
- boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded;
+ boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded && mStatusBarWindowHidden;
boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing;
boolean shouldHideIconsForBouncer = hideBecauseApp || hideBecauseKeyguard;
if (mHideIconsForBouncer != shouldHideIconsForBouncer) {
@@ -5058,6 +5058,7 @@
final boolean afterKeyguardGone = intent.isActivity()
&& PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
+ final boolean wasOccluded = mIsOccluded;
dismissKeyguardThenExecute(() -> {
// TODO: Some of this code may be able to move to NotificationEntryManager.
if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
@@ -5121,7 +5122,7 @@
remoteInputText.toString());
}
RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
- row);
+ row, wasOccluded);
try {
if (adapter != null) {
ActivityManager.getService()
@@ -5168,6 +5169,7 @@
if (mStatusBarKeyguardViewManager.isShowing()
&& mStatusBarKeyguardViewManager.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
+ collapsePanel(true /* animate */);
} else {
new Thread(runnable).start();
}
@@ -5252,7 +5254,7 @@
int launchResult = TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
- mActivityLaunchAnimator.getLaunchAnimation(row)),
+ mActivityLaunchAnimator.getLaunchAnimation(row, mIsOccluded)),
new UserHandle(UserHandle.getUserId(appUid)));
mActivityLaunchAnimator.setLaunchResult(launchResult);
if (shouldCollapse()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 06a56ff..e9bdc68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -22,14 +22,12 @@
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewPropertyAnimator;
import android.widget.FrameLayout;
import com.android.internal.util.Preconditions;
-import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.function.Consumer;
@@ -39,13 +37,9 @@
public class BrightnessMirrorController
implements CallbackController<BrightnessMirrorController.BrightnessMirrorListener> {
- private final static long TRANSITION_DURATION_OUT = 150;
- private final static long TRANSITION_DURATION_IN = 200;
-
private final StatusBarWindowView mStatusBarWindow;
- private final NotificationStackScrollLayout mStackScroller;
private final Consumer<Boolean> mVisibilityCallback;
- private final View mNotificationPanel;
+ private final NotificationPanelView mNotificationPanel;
private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>();
private final int[] mInt2Cache = new int[2];
private View mBrightnessMirror;
@@ -55,38 +49,21 @@
mStatusBarWindow = statusBarWindow;
mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
- mStackScroller = statusBarWindow.findViewById(R.id.notification_stack_scroller);
+ mNotificationPanel.setPanelAlphaEndAction(() -> {
+ mBrightnessMirror.setVisibility(View.INVISIBLE);
+ });
mVisibilityCallback = visibilityCallback;
}
public void showMirror() {
mBrightnessMirror.setVisibility(View.VISIBLE);
- mStackScroller.setFadingOut(true);
mVisibilityCallback.accept(true);
- outAnimation(mNotificationPanel.animate())
- .withLayer();
+ mNotificationPanel.setPanelAlpha(0, true /* animate */);
}
public void hideMirror() {
mVisibilityCallback.accept(false);
- inAnimation(mNotificationPanel.animate())
- .withLayer()
- .withEndAction(() -> {
- mBrightnessMirror.setVisibility(View.INVISIBLE);
- mStackScroller.setFadingOut(false);
- });
- }
-
- private ViewPropertyAnimator outAnimation(ViewPropertyAnimator a) {
- return a.alpha(0.0f)
- .setDuration(TRANSITION_DURATION_OUT)
- .setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction(null);
- }
- private ViewPropertyAnimator inAnimation(ViewPropertyAnimator a) {
- return a.alpha(1.0f)
- .setDuration(TRANSITION_DURATION_IN)
- .setInterpolator(Interpolators.ALPHA_IN);
+ mNotificationPanel.setPanelAlpha(255, true /* animate */);
}
public void setLocation(View original) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
index 2f6e658..47df226 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java
@@ -31,6 +31,7 @@
public long duration;
public long delay;
private ArrayMap<Property, Interpolator> mInterpolatorMap;
+ private AnimatorListenerAdapter mAnimatorListenerAdapter;
/**
* @return an animation filter for this animation.
@@ -48,7 +49,12 @@
* @return a listener that should be run whenever any property finished its animation
*/
public AnimatorListenerAdapter getAnimationFinishListener() {
- return null;
+ return mAnimatorListenerAdapter;
+ }
+
+ public AnimationProperties setAnimationFinishListener(AnimatorListenerAdapter listener) {
+ mAnimatorListenerAdapter = listener;
+ return this;
}
public boolean wasAdded(View view) {
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 55ec142..45fa44c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -269,6 +269,12 @@
updateGroupOverflow();
row.setContentTransformationAmount(0, false /* isLastChild */);
+ // It doesn't make sense to keep old animations around, lets cancel them!
+ ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+ if (viewState != null) {
+ viewState.cancelAnimations(row);
+ row.cancelAppearDrawing();
+ }
}
public void removeNotification(ExpandableNotificationRow row) {
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 236ead0..ee70019 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -98,6 +98,7 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -292,7 +293,7 @@
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
- private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
+ private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
private HeadsUpManagerPhone mHeadsUpManager;
@@ -413,6 +414,7 @@
private int mAntiBurnInOffsetX;
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
+ private HeadsUpAppearanceController mHeadsUpAppearanceController;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -727,7 +729,7 @@
}
private void updateAlgorithmLayoutMinHeight() {
- mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() || isHeadsUpTransition()
+ mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
? getLayoutMinHeight() : 0);
}
@@ -2451,7 +2453,6 @@
}
int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
int finalBottom = finalTranslationY + finalHeight - lastView.getClipBottomAmount();
- finalBottom = Math.min(finalBottom, getHeight());
if (mAnimateNextBackgroundBottom
|| mBottomAnimator == null && mCurrentBounds.bottom == finalBottom
|| mBottomAnimator != null && mEndAnimationRect.bottom == finalBottom) {
@@ -2460,7 +2461,6 @@
} else {
bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
- lastView.getClipBottomAmount());
- bottom = Math.min(bottom, getHeight());
}
} else {
top = mTopPadding;
@@ -2473,7 +2473,7 @@
top = Math.max(0, top);
}
mBackgroundBounds.top = top;
- mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
+ mBackgroundBounds.bottom = Math.max(bottom, top);
}
private ActivatableNotificationView getFirstPinnedHeadsUp() {
@@ -2831,8 +2831,8 @@
return false;
}
if (isClickedHeadsUp(child)) {
- // An animation is already running, add it to the Overlay
- mClearOverlayViewsWhenFinished.add(child);
+ // An animation is already running, add it transiently
+ mClearTransientViewsWhenFinished.add((ExpandableView) child);
return true;
}
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
@@ -3079,8 +3079,10 @@
@Override
public void bindRow(ExpandableNotificationRow row) {
- row.setHeadsUpAnimatingAwayListener(animatingAway
- -> mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway));
+ row.setHeadsUpAnimatingAwayListener(animatingAway -> {
+ mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
+ mHeadsUpAppearanceController.updateHeader(row.getEntry());
+ });
}
@Override
@@ -3202,6 +3204,7 @@
if (row.isChildInGroup()) {
// We can otherwise get stuck in there if it was just isolated
row.setHeadsUpAnimatingAway(false);
+ continue;
}
} else {
ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
@@ -3622,7 +3625,7 @@
}
private void clearTemporaryViews() {
- // lets make sure nothing is in the overlay / transient anymore
+ // lets make sure nothing is transient anymore
clearTemporaryViewsInGroup(this);
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -3637,9 +3640,6 @@
while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
viewGroup.removeTransientView(viewGroup.getTransientView(0));
}
- if (viewGroup != null) {
- viewGroup.getOverlay().clear();
- }
}
public void onPanelTrackingStarted() {
@@ -3747,7 +3747,7 @@
setAnimationRunning(false);
requestChildrenUpdate();
runAnimationFinishedRunnables();
- clearViewOverlays();
+ clearTransient();
clearHeadsUpDisappearRunning();
}
@@ -3766,11 +3766,11 @@
}
}
- private void clearViewOverlays() {
- for (View view : mClearOverlayViewsWhenFinished) {
- StackStateAnimator.removeFromOverlay(view);
+ private void clearTransient() {
+ for (ExpandableView view : mClearTransientViewsWhenFinished) {
+ StackStateAnimator.removeTransientView(view);
}
- mClearOverlayViewsWhenFinished.clear();
+ mClearTransientViewsWhenFinished.clear();
}
private void runAnimationFinishedRunnables() {
@@ -4638,6 +4638,11 @@
mExpandedHeightListeners.remove(listener);
}
+ public void setHeadsUpAppearanceController(
+ HeadsUpAppearanceController headsUpAppearanceController) {
+ mHeadsUpAppearanceController = headsUpAppearanceController;
+ }
+
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f4d7f8d..85f33d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -478,6 +478,22 @@
childState.hidden = false;
}
}
+ // Let's hide all the views if we are not expanded. the views might otherwise be visible
+ // in the headsup area if a view was swiped away
+ if (!mIsExpanded) {
+ for (int i = 0; i < childCount; i++) {
+ boolean visible = false;
+ View child = algorithmState.visibleChildren.get(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ visible = row.isHeadsUp() || row.isHeadsUpAnimatingAway();
+ }
+ if (!visible) {
+ ExpandableViewState childState = resultState.getViewStateForView(child);
+ childState.hidden = true;
+ }
+ }
+ }
}
private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index d01db14..4e8fcac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -419,9 +419,6 @@
}, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- // A race condition can trigger the view to be added to the overlay even though
- // it was fully swiped out. So let's remove it
- mHostLayout.getOverlay().remove(changingView);
if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
&& changingView.getTransientContainer() != null) {
changingView.getTransientContainer().removeTransientView(changingView);
@@ -469,8 +466,9 @@
? ANIMATION_DELAY_HEADS_UP_CLICKED
: 0;
if (changingView.getParent() == null) {
- // This notification was actually removed, so we need to add it to the overlay
- mHostLayout.getOverlay().add(changingView);
+ // This notification was actually removed, so we need to add it transiently
+ mHostLayout.addTransientView(changingView, 0);
+ changingView.setTransientContainer(mHostLayout);
mTmpState.initFrom(changingView);
mTmpState.yTranslation = 0;
// We temporarily enable Y animations, the real filter will be combined
@@ -479,10 +477,7 @@
mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
mTmpState.animateTo(changingView, mAnimationProperties);
- endRunnable = () -> {
- // remove the temporary overlay
- removeFromOverlay(changingView);
- };
+ endRunnable = () -> removeTransientView(changingView);
}
float targetLocation = 0;
boolean needsAnimation = true;
@@ -517,19 +512,12 @@
}
}
- private static void removeTransientView(ExpandableView viewToRemove) {
+ public static void removeTransientView(ExpandableView viewToRemove) {
if (viewToRemove.getTransientContainer() != null) {
viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
}
}
- public static void removeFromOverlay(View changingView) {
- ViewGroup parent = (ViewGroup) changingView.getParent();
- if (parent != null) {
- parent.removeView(changingView);
- }
- }
-
public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
final boolean isRubberbanded) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 611d4eb..1bdb7ad 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -54,6 +54,7 @@
// shouldn't be reset with tuner settings.
private static final String[] RESET_BLACKLIST = new String[] {
QSTileHost.TILES_SETTING,
+ Settings.Secure.DOZE_ALWAYS_ON
};
private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 21483aa..d46a974 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import android.content.Context;
import android.content.Intent;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -35,7 +36,12 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+// We must run on the main looper because KeyguardUpdateMonitor#mHandler is initialized with
+// new Handler(Looper.getMainLooper()).
+//
+// Using the main looper should be avoided whenever possible, please don't copy this over to
+// new tests.
+@RunWithLooper(setAsMainLooper = true)
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
@@ -49,24 +55,39 @@
public void testIgnoresSimStateCallback_rebroadcast() {
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- AtomicBoolean simStateChanged = new AtomicBoolean(false);
- KeyguardUpdateMonitor keyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
- @Override
- protected void handleSimStateChange(int subId, int slotId,
- IccCardConstants.State state) {
- simStateChanged.set(true);
- super.handleSimStateChange(subId, slotId, state);
- }
- };
+ TestableKeyguardUpdateMonitor keyguardUpdateMonitor =
+ new TestableKeyguardUpdateMonitor(getContext());
keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
- Assert.assertTrue("onSimStateChanged not called", simStateChanged.get());
+ Assert.assertTrue("onSimStateChanged not called",
+ keyguardUpdateMonitor.hasSimStateJustChanged());
intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
- simStateChanged.set(false);
keyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
- Assert.assertFalse("onSimStateChanged should have been skipped", simStateChanged.get());
+ Assert.assertFalse("onSimStateChanged should have been skipped",
+ keyguardUpdateMonitor.hasSimStateJustChanged());
+ }
+
+ private class TestableKeyguardUpdateMonitor extends KeyguardUpdateMonitor {
+ AtomicBoolean mSimStateChanged = new AtomicBoolean(false);
+
+ protected TestableKeyguardUpdateMonitor(Context context) {
+ super(context);
+ // Avoid race condition when unexpected broadcast could be received.
+ context.unregisterReceiver(mBroadcastReceiver);
+ }
+
+ public boolean hasSimStateJustChanged() {
+ return mSimStateChanged.getAndSet(false);
+ }
+
+ @Override
+ protected void handleSimStateChange(int subId, int slotId,
+ IccCardConstants.State state) {
+ mSimStateChanged.set(true);
+ super.handleSimStateChange(subId, slotId, state);
+ }
}
}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 95dc3ab..083598d 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -188,6 +188,23 @@
ACCESS_METHOD_LINKING = 3; // never logged, included for completeness
}
+ enum HardwareType {
+ HARDWARE_UNKNOWN = 0;
+ HARDWARE_MICROPHONE = 1;
+ HARDWARE_CODEC = 2;
+ HARDWARE_SPEAKER = 3;
+ HARDWARE_FINGERPRINT = 4;
+ }
+
+ enum HardwareFailureCode {
+ HARDWARE_FAILURE_UNKNOWN = 0;
+ HARDWARE_FAILURE_COMPLETE = 1;
+ HARDWARE_FAILURE_SPEAKER_HIGH_Z = 2;
+ HARDWARE_FAILURE_SPEAKER_SHORT = 3;
+ HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN = 4;
+ HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5;
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -5296,11 +5313,11 @@
// OS: P
FIELD_DURATION_MILLIS = 1304;
- // Battery level in uA (0 - ~3,000,000 depending on device) when the current "mode" started.
+ // Battery level in uAh (0 - ~3,000,000 depending on device) when the current "mode" started.
// OS: P
FIELD_START_BATTERY_UA = 1305;
- // Battery level in uA (0 - ~3,000,000 depending on device) when this event was created.
+ // Battery level in uAh (0 - ~3,000,000 depending on device) when this event was created.
// OS: P
FIELD_END_BATTERY_UA = 1306;
@@ -5329,7 +5346,6 @@
// OS: P
FIELD_QS_MODE = 1311;
-
// OPEN: Settings->Developer Options->Default USB
// CATEGORY: SETTINGS
// OS: P
@@ -5733,8 +5749,8 @@
// OS: P
FIELD_HIDDEN_API_SIGNATURE = 1394;
- // This value should never appear in log outputs - it is reserved for
- // internal platform metrics use.
+ // The number of items in the shade when this notification event was logged.
+ // OS: P
NOTIFICATION_SHADE_COUNT = 1395;
// ACTION: DND Settings > What to block
@@ -5852,6 +5868,112 @@
// 3 = Wireless
FIELD_PLUG_TYPE = 1421;
+ // ACTION: USB-C Connector connected.
+ // CATEGORY: OTHER
+ // OS: P
+ ACTION_USB_CONNECTOR_CONNECTED = 1422;
+
+ // ACTION: USB-C Connector disconnected.
+ // CATEGORY: OTHER
+ // OS: P
+ // uses FIELD_DURATION_MILLIS for connected duration
+ ACTION_USB_CONNECTOR_DISCONNECTED = 1423;
+
+ // ACTION: USB-C Audio device connected
+ // CATEGORY: OTHER
+ // OS: P
+ ACTION_USB_AUDIO_CONNECTED = 1424;
+
+ // FIELD: VIDPID of connected USB Audio device
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_USB_AUDIO_VIDPID = 1425;
+
+ // ACTION: USB-C Audio device disconnected
+ // CATEGORY: OTHER
+ // OS: P
+ // use FIELD_DURATION_MILLIS for time
+ ACTION_USB_AUDIO_DISCONNECTED = 1426;
+
+ // ACTION: Hardware failure event
+ // CATEGORY: OTHER
+ // OS: P
+ ACTION_HARDWARE_FAILED = 1427;
+
+ // FIELD: Hardware failure category
+ // CATEGORY: OTHER
+ // OS: P
+ // Uses enum HardwareType
+ FIELD_HARDWARE_TYPE = 1428;
+
+ // FIELD: Hardware failure category
+ // CATEGORY: OTHER
+ // OS: P
+ // Uses enum HardwareFailureCode
+ FIELD_HARDWARE_FAILURE_CODE = 1429;
+
+ // ACTION: Physical drop event
+ // CATEGORY: OTHER
+ // OS: P
+ // use FIELD_DURATION_MILLIS for time
+ ACTION_PHYSICAL_DROP = 1430;
+
+ // FIELD: Confidence in detection of drop, in integer percentage.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_CONFIDENCE_PERCENT = 1431;
+
+ // FIELD: Detected bounce acceleration, in 1/1000th of a G.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_ACCEL_MILLI_G = 1432;
+
+ // ACTION: Battery health snapshot
+ // CATEGORY: OTHER
+ // OS: P
+ // uses FIELD_END_BATTERY_PERCENT for batt %
+ // uses FIELD_END_BATTERY_UA for instantaneous current load
+ ACTION_BATTERY_HEALTH = 1433;
+
+ // FIELD: Battery health snapshot type - min daily voltage, resistance, etc.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE = 1434;
+
+ // FIELD: Battery temperature at snapshop.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_BATTERY_TEMPERATURE = 1435;
+
+ // FIELD: Battery voltage at snapshot.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_BATTERY_VOLTAGE = 1436;
+
+ // FIELD: Battery open circuit voltage at snapshot.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE = 1437;
+
+ // ACTION: Battery charge cycles
+ // Number of times the battery has charged beyond a
+ // fractional threshold of full capacity.
+ // CATEGORY: OTHER
+ // OS: P
+ ACTION_BATTERY_CHARGE_CYCLES = 1438;
+
+ // FIELD: Battery charge cycles
+ // Number of times the battery has charged beyond a
+ // fractional threshold of full capacity.
+ // CATEGORY: OTHER
+ // OS: P
+ FIELD_BATTERY_CHARGE_CYCLES = 1439;
+
+ // ACTION: Hush gesture - volume up + power button
+ // CATEGORY: GLOBAL_SYSTEM_UI
+ // OS: P
+ ACTION_HUSH_GESTURE = 1440;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index f9af31c..a9a14ca 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -184,19 +184,19 @@
NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED = 41;
// Inform the user that a wrong password was detected while attempting to connect
- // to a Wi-Fi network
+ // to a wifi network
NOTE_WIFI_WRONG_PASSWORD = 42;
// Inform the user that Wifi Wake will re-enable wifi when possible
NOTE_WIFI_WAKE_ONBOARD = 43;
- // Inform the user that Wifi Wake has automatically re-enabled Wifi
+ // Inform the user that Wifi Wake has automatically re-enabled wifi
NOTE_WIFI_WAKE_TURNED_BACK_ON = 44;
// Inform the user that unexpectedly rapid network usage is happening
NOTE_NET_RAPID = 45;
- // Notify the user that carrier Wi-Fi networks are available.
+ // Notify the user that carrier wifi networks are available.
// Package: android
NOTE_CARRIER_NETWORK_AVAILABLE = 46;
@@ -248,10 +248,10 @@
// Package: com.android.systemui
NOTE_TV_PIP = 1100;
- // Extreme battery saver notifiaction.
+ // Extreme battery saver notification.
NOTE_BATTERY_SAVER_WARNING = 1200;
- // Notify the user that open Wi-Fi networks are available.
+ // Notify the user that open wifi networks are available.
// Package: android
NOTE_NETWORK_AVAILABLE = 17303299;
@@ -261,7 +261,6 @@
// Notify the user about public volume state changes..
// Package: com.android.systemui
-
NOTE_STORAGE_PUBLIC = 0x53505542; // 1397773634
// Notify the user about private volume state changes.
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 8d2d4a3..db8ad12 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -106,7 +106,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -114,7 +113,6 @@
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;
-import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -839,6 +837,12 @@
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
+ // Instant apps cannot host app widgets.
+ if (mSecurityPolicy.isInstantAppLocked(callingPackage, userId)) {
+ Slog.w(TAG, "Instant package " + callingPackage + " cannot host app widgets");
+ return ParceledListSlice.emptyList();
+ }
+
ensureGroupStateLoadedLocked(userId);
// NOTE: The lookup is enforcing security across users by making
@@ -905,6 +909,12 @@
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
+ // Instant apps cannot host app widgets.
+ if (mSecurityPolicy.isInstantAppLocked(callingPackage, userId)) {
+ Slog.w(TAG, "Instant package " + callingPackage + " cannot host app widgets");
+ return AppWidgetManager.INVALID_APPWIDGET_ID;
+ }
+
ensureGroupStateLoadedLocked(userId);
if (mNextAppWidgetIds.indexOfKey(userId) < 0) {
@@ -1636,6 +1646,13 @@
@Override
public boolean isRequestPinAppWidgetSupported() {
+ synchronized (mLock) {
+ if (mSecurityPolicy.isCallerInstantAppLocked()) {
+ Slog.w(TAG, "Instant uid " + Binder.getCallingUid()
+ + " query information about app widgets");
+ return false;
+ }
+ }
return LocalServices.getService(ShortcutServiceInternal.class)
.isRequestPinItemSupported(UserHandle.getCallingUserId(),
LauncherApps.PinItemRequest.REQUEST_TYPE_APPWIDGET);
@@ -1686,6 +1703,12 @@
}
synchronized (mLock) {
+ if (mSecurityPolicy.isCallerInstantAppLocked()) {
+ Slog.w(TAG, "Instant uid " + Binder.getCallingUid()
+ + " cannot access widget providers");
+ return ParceledListSlice.emptyList();
+ }
+
ensureGroupStateLoadedLocked(userId);
ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>();
@@ -3667,6 +3690,35 @@
"hasBindAppWidgetPermission packageName=" + packageName);
}
+ public boolean isCallerInstantAppLocked() {
+ final int callingUid = Binder.getCallingUid();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final String[] uidPackages = mPackageManager.getPackagesForUid(callingUid);
+ if (!ArrayUtils.isEmpty(uidPackages)) {
+ return mPackageManager.isInstantApp(uidPackages[0],
+ UserHandle.getCallingUserId());
+ }
+ } catch (RemoteException e) {
+ /* ignore - same process */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return false;
+ }
+
+ public boolean isInstantAppLocked(String packageName, int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mPackageManager.isInstantApp(packageName, userId);
+ } catch (RemoteException e) {
+ /* ignore - same process */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return false;
+ }
+
public void enforceCallFromPackage(String packageName) {
mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 9b833f7..21398e3 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -588,7 +588,7 @@
private void addCompatibilityModeRequestsLocked(@NonNull AutofillManagerServiceImpl service
, int userId) {
- mAutofillCompatState.reset();
+ mAutofillCompatState.reset(userId);
final ArrayMap<String, Long> compatPackages =
service.getCompatibilityPackagesLocked();
if (compatPackages == null || compatPackages.isEmpty()) {
@@ -721,6 +721,9 @@
static final class AutofillCompatState {
private final Object mLock = new Object();
+ /**
+ * Map of app->compat_state per user.
+ */
@GuardedBy("mLock")
private SparseArray<ArrayMap<String, PackageCompatState>> mUserSpecs;
@@ -787,11 +790,17 @@
}
}
- void reset() {
+ void reset(int userId) {
synchronized (mLock) {
if (mUserSpecs != null) {
- mUserSpecs.clear();
- mUserSpecs = null;
+ mUserSpecs.delete(userId);
+ final int newSize = mUserSpecs.size();
+ if (newSize == 0) {
+ if (sVerbose) Slog.v(TAG, "reseting mUserSpecs");
+ mUserSpecs = null;
+ } else {
+ if (sVerbose) Slog.v(TAG, "mUserSpecs down to " + newSize);
+ }
}
}
}
@@ -806,7 +815,7 @@
for (int i = 0; i < mUserSpecs.size(); i++) {
final int user = mUserSpecs.keyAt(i);
pw.print(prefix); pw.print("User: "); pw.println(user);
- final ArrayMap<String,PackageCompatState> perUser = mUserSpecs.get(i);
+ final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
for (int j = 0; j < perUser.size(); j++) {
final String packageName = perUser.keyAt(j);
final PackageCompatState state = perUser.valueAt(j);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 4c914c2..61b1dcc 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -95,11 +95,12 @@
import java.util.List;
import java.util.Map;
+import static android.app.AppOpsManager._NUM_UID_STATE;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
-import static android.app.AppOpsManager._NUM_UID_STATE;
+import static android.app.AppOpsManager.UID_STATE_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
@@ -213,15 +214,31 @@
*/
private final class Constants extends ContentObserver {
// Key names stored in the settings value.
- private static final String KEY_STATE_SETTLE_TIME = "state_settle_time";
+ private static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
+ private static final String KEY_FG_SERVICE_STATE_SETTLE_TIME
+ = "fg_service_state_settle_time";
+ private static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
/**
- * How long we want for a drop in uid state to settle before applying it.
+ * How long we want for a drop in uid state from top to settle before applying it.
* @see Settings.Global#APP_OPS_CONSTANTS
- * @see #KEY_STATE_SETTLE_TIME
+ * @see #KEY_TOP_STATE_SETTLE_TIME
*/
- public long STATE_SETTLE_TIME;
+ public long TOP_STATE_SETTLE_TIME;
+ /**
+ * How long we want for a drop in uid state from foreground to settle before applying it.
+ * @see Settings.Global#APP_OPS_CONSTANTS
+ * @see #KEY_FG_SERVICE_STATE_SETTLE_TIME
+ */
+ public long FG_SERVICE_STATE_SETTLE_TIME;
+
+ /**
+ * How long we want for a drop in uid state from background to settle before applying it.
+ * @see Settings.Global#APP_OPS_CONSTANTS
+ * @see #KEY_BG_STATE_SETTLE_TIME
+ */
+ public long BG_STATE_SETTLE_TIME;
private final KeyValueListParser mParser = new KeyValueListParser(',');
private ContentResolver mResolver;
@@ -256,16 +273,24 @@
// with defaults.
Slog.e(TAG, "Bad app ops settings", e);
}
- STATE_SETTLE_TIME = mParser.getDurationMillis(
- KEY_STATE_SETTLE_TIME, 10 * 1000L);
+ TOP_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_TOP_STATE_SETTLE_TIME, 30 * 1000L);
+ FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_FG_SERVICE_STATE_SETTLE_TIME, 10 * 1000L);
+ BG_STATE_SETTLE_TIME = mParser.getDurationMillis(
+ KEY_BG_STATE_SETTLE_TIME, 1 * 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.print(" "); pw.print(KEY_TOP_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(TOP_STATE_SETTLE_TIME, pw);
+ pw.print(" "); pw.print(KEY_FG_SERVICE_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(FG_SERVICE_STATE_SETTLE_TIME, pw);
+ pw.print(" "); pw.print(KEY_BG_STATE_SETTLE_TIME); pw.print("=");
+ TimeUtils.formatDuration(BG_STATE_SETTLE_TIME, pw);
pw.println();
}
}
@@ -304,7 +329,7 @@
int evalMode(int mode) {
if (mode == AppOpsManager.MODE_FOREGROUND) {
- return state <= UID_STATE_FOREGROUND_SERVICE
+ return state <= UID_STATE_LAST_NON_RESTRICTED
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
}
return mode;
@@ -728,14 +753,22 @@
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.
+ if (newState < uidState.state || newState <= UID_STATE_LAST_NON_RESTRICTED) {
+ // We are moving to a more important state, or the new state is in the
+ // foreground, then always do it immediately.
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() +
- mConstants.STATE_SETTLE_TIME;
+ final long settleTime;
+ if (uidState.state <= UID_STATE_TOP) {
+ settleTime = mConstants.TOP_STATE_SETTLE_TIME;
+ } else if (uidState.state <= UID_STATE_FOREGROUND_SERVICE) {
+ settleTime = mConstants.FG_SERVICE_STATE_SETTLE_TIME;
+ } else {
+ settleTime = mConstants.BG_STATE_SETTLE_TIME;
+ }
+ uidState.pendingStateCommitTime = SystemClock.uptimeMillis() + settleTime;
}
if (uidState.startNesting != 0) {
// There is some actively running operation... need to find it
@@ -1858,9 +1891,11 @@
}
private void commitUidPendingStateLocked(UidState uidState) {
+ final boolean lastForeground = uidState.state <= UID_STATE_LAST_NON_RESTRICTED;
+ final boolean nowForeground = uidState.pendingState <= UID_STATE_LAST_NON_RESTRICTED;
uidState.state = uidState.pendingState;
uidState.pendingStateCommitTime = 0;
- if (uidState.hasForegroundWatchers) {
+ if (uidState.hasForegroundWatchers && lastForeground != nowForeground) {
for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) {
if (!uidState.foregroundOps.valueAt(fgi)) {
continue;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 00302b2..183be9b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2795,7 +2795,7 @@
final String uuid = null;
final String state = Environment.MEDIA_REMOVED;
- res.add(0, new StorageVolume(id, path,
+ res.add(0, new StorageVolume(id, path, path,
description, primary, removable, emulated,
allowMassStorage, maxFileSize, owner, uuid, state));
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b32ece7..6f572df 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -476,12 +476,23 @@
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
- + " pkg=" + callingPackage);
+ + " pkg=" + callingPackage + " startFg?=" + fgRequired);
if (allowed == ActivityManager.APP_START_MODE_DELAYED || forceSilentAbort) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
+ if (forcedStandby) {
+ // This is an O+ app, but we might be here because the user has placed
+ // it under strict background restrictions. Don't punish the app if it's
+ // trying to do the right thing but we're denying it for that reason.
+ if (fgRequired) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.v(TAG, "Silently dropping foreground service launch due to FAS");
+ }
+ return null;
+ }
+ }
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
@@ -3592,6 +3603,21 @@
}
}
+ public void updateServiceApplicationInfoLocked(ApplicationInfo applicationInfo) {
+ final int userId = UserHandle.getUserId(applicationInfo.uid);
+ ServiceMap serviceMap = mServiceMap.get(userId);
+ if (serviceMap != null) {
+ ArrayMap<ComponentName, ServiceRecord> servicesByName = serviceMap.mServicesByName;
+ for (int j = servicesByName.size() - 1; j >= 0; j--) {
+ ServiceRecord serviceRecord = servicesByName.valueAt(j);
+ if (applicationInfo.packageName.equals(serviceRecord.appInfo.packageName)) {
+ serviceRecord.appInfo = applicationInfo;
+ serviceRecord.serviceInfo.applicationInfo = applicationInfo;
+ }
+ }
+ }
+ }
+
void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
"Context.startForegroundService() did not then call Service.startForeground(): "
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f83d9e6..70648c6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21455,6 +21455,7 @@
return ActivityManager.BROADCAST_SUCCESS;
}
mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+ mServices.updateServiceApplicationInfoLocked(aInfo);
sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
new String[] {ssp}, userId);
}
@@ -22011,12 +22012,13 @@
doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
+ if (doNext) {
+ r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true);
+ }
+ // updateOomAdjLocked() will be done here
+ trimApplicationsLocked();
}
- if (doNext) {
- r.queue.processNextBroadcast(false);
- }
- trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -25747,40 +25749,43 @@
final void trimApplications() {
synchronized (this) {
- int i;
+ trimApplicationsLocked();
+ }
+ }
- // First remove any unused application processes whose package
- // has been removed.
- for (i=mRemovedProcesses.size()-1; i>=0; i--) {
- final ProcessRecord app = mRemovedProcesses.get(i);
- if (app.activities.size() == 0 && app.recentTasks.size() == 0
- && app.curReceivers.isEmpty() && app.services.size() == 0) {
- Slog.i(
- TAG, "Exiting empty application process "
- + app.toShortString() + " ("
- + (app.thread != null ? app.thread.asBinder() : null)
- + ")\n");
- if (app.pid > 0 && app.pid != MY_PID) {
- app.kill("empty", false);
- } else if (app.thread != null) {
- try {
- app.thread.scheduleExit();
- } catch (Exception e) {
- // Ignore exceptions.
- }
- }
- cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
- mRemovedProcesses.remove(i);
-
- if (app.persistent) {
- addAppLocked(app.info, null, false, null /* ABI override */);
+ final void trimApplicationsLocked() {
+ // First remove any unused application processes whose package
+ // has been removed.
+ for (int i=mRemovedProcesses.size()-1; i>=0; i--) {
+ final ProcessRecord app = mRemovedProcesses.get(i);
+ if (app.activities.size() == 0 && app.recentTasks.size() == 0
+ && app.curReceivers.isEmpty() && app.services.size() == 0) {
+ Slog.i(
+ TAG, "Exiting empty application process "
+ + app.toShortString() + " ("
+ + (app.thread != null ? app.thread.asBinder() : null)
+ + ")\n");
+ if (app.pid > 0 && app.pid != MY_PID) {
+ app.kill("empty", false);
+ } else if (app.thread != null) {
+ try {
+ app.thread.scheduleExit();
+ } catch (Exception e) {
+ // Ignore exceptions.
}
}
- }
+ cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/);
+ mRemovedProcesses.remove(i);
- // Now update the oom adj for all processes.
- updateOomAdjLocked();
+ if (app.persistent) {
+ addAppLocked(app.info, null, false, null /* ABI override */);
+ }
+ }
}
+
+ // Now update the oom adj for all processes. Don't skip this, since other callers
+ // might be depending on it.
+ updateOomAdjLocked();
}
/** This method sends the specified signal to each of the persistent apps */
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d194db3..df1ee55 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1702,11 +1702,16 @@
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
String resultWho, int requestCode, int callingPid, int callingUid,
- String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
- ActivityRecord resultRecord, ActivityStack resultStack) {
+ String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask,
+ ProcessRecord callerApp, ActivityRecord resultRecord, ActivityStack resultStack) {
+ final boolean isCallerRecents = mService.getRecentTasks() != null &&
+ mService.getRecentTasks().isCallerRecents(callingUid);
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
callingUid);
- if (startAnyPerm == PERMISSION_GRANTED) {
+ if (startAnyPerm == PERMISSION_GRANTED || (isCallerRecents && launchingInTask)) {
+ // If the caller has START_ANY_ACTIVITY, ignore all checks below. In addition, if the
+ // caller is the recents component and we are specifically starting an activity in an
+ // existing task, then also allow the activity to be fully relaunched.
return true;
}
final int componentRestriction = getComponentRestrictionForCallingPackage(
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index bbf6e6c..ad434b4 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -713,7 +713,7 @@
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
- callerApp, resultRecord, resultStack);
+ inTask != null, callerApp, resultRecord, resultStack);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
@@ -1587,6 +1587,7 @@
}
}
} else if (mOptions.getAvoidMoveToFront()) {
+ mDoResume = false;
mAvoidMoveToFront = true;
}
}
@@ -1929,7 +1930,7 @@
// Need to update mTargetStack because if task was moved out of it, the original stack may
// be destroyed.
mTargetStack = intentActivity.getStack();
- if (!mAvoidMoveToFront && !mMovedToFront && mDoResume) {
+ if (!mMovedToFront && mDoResume) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
+ " from " + intentActivity);
mTargetStack.moveToFront("intentActivityFound");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index cc3a887..c9a26cb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -271,7 +271,7 @@
}
private final void processCurBroadcastLocked(BroadcastRecord r,
- ProcessRecord app) throws RemoteException {
+ ProcessRecord app, boolean skipOomAdj) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
if (app.thread == null) {
@@ -287,7 +287,9 @@
app.curReceivers.add(r);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.updateLruProcessLocked(app, false, null);
- mService.updateOomAdjLocked();
+ if (!skipOomAdj) {
+ mService.updateOomAdjLocked();
+ }
// Tell the application to launch this receiver.
r.intent.setComponent(r.curComponent);
@@ -328,7 +330,7 @@
}
try {
mPendingBroadcast = null;
- processCurBroadcastLocked(br, app);
+ processCurBroadcastLocked(br, app, false);
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
@@ -819,577 +821,581 @@
}
final void processNextBroadcast(boolean fromMsg) {
- synchronized(mService) {
- BroadcastRecord r;
+ synchronized (mService) {
+ processNextBroadcastLocked(fromMsg, false);
+ }
+ }
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
- + mQueueName + "]: "
- + mParallelBroadcasts.size() + " parallel broadcasts, "
- + mOrderedBroadcasts.size() + " ordered broadcasts");
+ final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
+ BroadcastRecord r;
- mService.updateCpuStats();
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ + mQueueName + "]: "
+ + mParallelBroadcasts.size() + " parallel broadcasts, "
+ + mOrderedBroadcasts.size() + " ordered broadcasts");
- if (fromMsg) {
- mBroadcastsScheduled = false;
+ mService.updateCpuStats();
+
+ if (fromMsg) {
+ mBroadcastsScheduled = false;
+ }
+
+ // First, deliver any non-serialized broadcasts right away.
+ while (mParallelBroadcasts.size() > 0) {
+ r = mParallelBroadcasts.remove(0);
+ r.dispatchTime = SystemClock.uptimeMillis();
+ r.dispatchClockTime = System.currentTimeMillis();
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
}
- // First, deliver any non-serialized broadcasts right away.
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchClockTime = System.currentTimeMillis();
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
-
- final int N = r.receivers.size();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
- + mQueueName + "] " + r);
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering non-ordered on [" + mQueueName + "] to registered "
- + target + ": " + r);
- deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
- }
- addBroadcastToHistoryLocked(r);
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
- + mQueueName + "] " + r);
- }
-
- // Now take care of the next serialized one...
-
- // If we are waiting for a process to come up to handle the next
- // broadcast, then do nothing at this point. Just in case, we
- // check that the process we're waiting for still exists.
- if (mPendingBroadcast != null) {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "processNextBroadcast [" + mQueueName + "]: waiting for "
- + mPendingBroadcast.curApp);
-
- boolean isDead;
- if (mPendingBroadcast.curApp.pid > 0) {
- synchronized (mService.mPidsSelfLocked) {
- ProcessRecord proc = mService.mPidsSelfLocked.get(
- mPendingBroadcast.curApp.pid);
- isDead = proc == null || proc.crashing;
- }
- } else {
- final ProcessRecord proc = mService.mProcessNames.get(
- mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
- isDead = proc == null || !proc.pendingStart;
- }
- if (!isDead) {
- // It's still alive, so keep waiting
- return;
- } else {
- Slog.w(TAG, "pending app ["
- + mQueueName + "]" + mPendingBroadcast.curApp
- + " died before responding to broadcast");
- mPendingBroadcast.state = BroadcastRecord.IDLE;
- mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- }
- }
-
- boolean looped = false;
-
- do {
- if (mOrderedBroadcasts.size() == 0) {
- // No more broadcasts pending, so all done!
- mService.scheduleAppGcsLocked();
- if (looped) {
- // If we had finished the last ordered broadcast, then
- // make sure all processes have correct oom and sched
- // adjustments.
- mService.updateOomAdjLocked();
- }
- return;
- }
- r = mOrderedBroadcasts.get(0);
- boolean forceReceive = false;
-
- // Ensure that even if something goes awry with the timeout
- // detection, we catch "hung" broadcasts here, discard them,
- // and continue to make progress.
- //
- // This is only done if the system is ready so that PRE_BOOT_COMPLETED
- // receivers don't get executed with timeouts. They're intended for
- // one time heavy lifting after system upgrades and can take
- // significant amounts of time.
- int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mService.mProcessesReady && r.dispatchTime > 0) {
- long now = SystemClock.uptimeMillis();
- if ((numReceivers > 0) &&
- (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
- Slog.w(TAG, "Hung broadcast ["
- + mQueueName + "] discarded after timeout failure:"
- + " now=" + now
- + " dispatchTime=" + r.dispatchTime
- + " startTime=" + r.receiverTime
- + " intent=" + r.intent
- + " numReceivers=" + numReceivers
- + " nextReceiver=" + r.nextReceiver
- + " state=" + r.state);
- broadcastTimeoutLocked(false); // forcibly finish this broadcast
- forceReceive = true;
- r.state = BroadcastRecord.IDLE;
- }
- }
-
- if (r.state != BroadcastRecord.IDLE) {
- if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
- "processNextBroadcast("
- + mQueueName + ") called when not idle (state="
- + r.state + ")");
- return;
- }
-
- if (r.receivers == null || r.nextReceiver >= numReceivers
- || r.resultAbort || forceReceive) {
- // No more receivers for this broadcast! Send the final
- // result if requested...
- if (r.resultTo != null) {
- try {
- if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
- "Finishing broadcast [" + mQueueName + "] "
- + r.intent.getAction() + " app=" + r.callerApp);
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId);
- // Set this to null so that the reference
- // (local and remote) isn't kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- r.resultTo = null;
- Slog.w(TAG, "Failure ["
- + mQueueName + "] sending broadcast result of "
- + r.intent, e);
-
- }
- }
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
- cancelBroadcastTimeoutLocked();
-
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Finished with ordered broadcast " + r);
-
- // ... and on to the next...
- addBroadcastToHistoryLocked(r);
- if (r.intent.getComponent() == null && r.intent.getPackage() == null
- && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
- // This was an implicit broadcast... let's record it for posterity.
- mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
- r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
- }
- mOrderedBroadcasts.remove(0);
- r = null;
- looped = true;
- continue;
- }
- } while (r == null);
-
- // Get the next receiver...
- int recIdx = r.nextReceiver++;
-
- // Keep track of when this receiver started, and make sure there
- // is a timeout message pending to kill it if need be.
- r.receiverTime = SystemClock.uptimeMillis();
- if (recIdx == 0) {
- r.dispatchTime = r.receiverTime;
- r.dispatchClockTime = System.currentTimeMillis();
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
- + mQueueName + "] " + r);
- }
- if (! mPendingBroadcastTimeoutMessage) {
- long timeoutTime = r.receiverTime + mTimeoutPeriod;
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Submitting BROADCAST_TIMEOUT_MSG ["
- + mQueueName + "] for " + r + " at " + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- }
-
- final BroadcastOptions brOptions = r.options;
- final Object nextReceiver = r.receivers.get(recIdx);
-
- if (nextReceiver instanceof BroadcastFilter) {
- // Simple case: this is a registered receiver who gets
- // a direct call.
- BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+ final int N = r.receivers.size();
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ + mQueueName + "] " + r);
+ for (int i=0; i<N; i++) {
+ Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering ordered ["
- + mQueueName + "] to registered "
- + filter + ": " + r);
- deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
- if (r.receiver == null || !r.ordered) {
- // The receiver has already finished, so schedule to
- // process the next one.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
- + mQueueName + "]: ordered="
- + r.ordered + " receiver=" + r.receiver);
- r.state = BroadcastRecord.IDLE;
- scheduleBroadcastsLocked();
- } else {
- if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
- scheduleTempWhitelistLocked(filter.owningUid,
- brOptions.getTemporaryAppWhitelistDuration(), r);
- }
+ "Delivering non-ordered on [" + mQueueName + "] to registered "
+ + target + ": " + r);
+ deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
+ }
+ addBroadcastToHistoryLocked(r);
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ + mQueueName + "] " + r);
+ }
+
+ // Now take care of the next serialized one...
+
+ // If we are waiting for a process to come up to handle the next
+ // broadcast, then do nothing at this point. Just in case, we
+ // check that the process we're waiting for still exists.
+ if (mPendingBroadcast != null) {
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "processNextBroadcast [" + mQueueName + "]: waiting for "
+ + mPendingBroadcast.curApp);
+
+ boolean isDead;
+ if (mPendingBroadcast.curApp.pid > 0) {
+ synchronized (mService.mPidsSelfLocked) {
+ ProcessRecord proc = mService.mPidsSelfLocked.get(
+ mPendingBroadcast.curApp.pid);
+ isDead = proc == null || proc.crashing;
+ }
+ } else {
+ final ProcessRecord proc = mService.mProcessNames.get(
+ mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
+ isDead = proc == null || !proc.pendingStart;
+ }
+ if (!isDead) {
+ // It's still alive, so keep waiting
+ return;
+ } else {
+ Slog.w(TAG, "pending app ["
+ + mQueueName + "]" + mPendingBroadcast.curApp
+ + " died before responding to broadcast");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ }
+ }
+
+ boolean looped = false;
+
+ do {
+ if (mOrderedBroadcasts.size() == 0) {
+ // No more broadcasts pending, so all done!
+ mService.scheduleAppGcsLocked();
+ if (looped) {
+ // If we had finished the last ordered broadcast, then
+ // make sure all processes have correct oom and sched
+ // adjustments.
+ mService.updateOomAdjLocked();
}
return;
}
+ r = mOrderedBroadcasts.get(0);
+ boolean forceReceive = false;
- // Hard case: need to instantiate the receiver, possibly
- // starting its application process to host it.
+ // Ensure that even if something goes awry with the timeout
+ // detection, we catch "hung" broadcasts here, discard them,
+ // and continue to make progress.
+ //
+ // This is only done if the system is ready so that PRE_BOOT_COMPLETED
+ // receivers don't get executed with timeouts. They're intended for
+ // one time heavy lifting after system upgrades and can take
+ // significant amounts of time.
+ int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (mService.mProcessesReady && r.dispatchTime > 0) {
+ long now = SystemClock.uptimeMillis();
+ if ((numReceivers > 0) &&
+ (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
+ Slog.w(TAG, "Hung broadcast ["
+ + mQueueName + "] discarded after timeout failure:"
+ + " now=" + now
+ + " dispatchTime=" + r.dispatchTime
+ + " startTime=" + r.receiverTime
+ + " intent=" + r.intent
+ + " numReceivers=" + numReceivers
+ + " nextReceiver=" + r.nextReceiver
+ + " state=" + r.state);
+ broadcastTimeoutLocked(false); // forcibly finish this broadcast
+ forceReceive = true;
+ r.state = BroadcastRecord.IDLE;
+ }
+ }
- ResolveInfo info =
- (ResolveInfo)nextReceiver;
- ComponentName component = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
+ if (r.state != BroadcastRecord.IDLE) {
+ if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
+ "processNextBroadcast("
+ + mQueueName + ") called when not idle (state="
+ + r.state + ")");
+ return;
+ }
- boolean skip = false;
- if (brOptions != null &&
- (info.activityInfo.applicationInfo.targetSdkVersion
- < brOptions.getMinManifestReceiverApiLevel() ||
- info.activityInfo.applicationInfo.targetSdkVersion
- > brOptions.getMaxManifestReceiverApiLevel())) {
+ if (r.receivers == null || r.nextReceiver >= numReceivers
+ || r.resultAbort || forceReceive) {
+ // No more receivers for this broadcast! Send the final
+ // result if requested...
+ if (r.resultTo != null) {
+ try {
+ if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
+ "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
+ performReceiveLocked(r.callerApp, r.resultTo,
+ new Intent(r.intent), r.resultCode,
+ r.resultData, r.resultExtras, false, false, r.userId);
+ // Set this to null so that the reference
+ // (local and remote) isn't kept in the mBroadcastHistory.
+ r.resultTo = null;
+ } catch (RemoteException e) {
+ r.resultTo = null;
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + r.intent, e);
+
+ }
+ }
+
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
+ cancelBroadcastTimeoutLocked();
+
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "Finished with ordered broadcast " + r);
+
+ // ... and on to the next...
+ addBroadcastToHistoryLocked(r);
+ if (r.intent.getComponent() == null && r.intent.getPackage() == null
+ && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ // This was an implicit broadcast... let's record it for posterity.
+ mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
+ r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
+ }
+ mOrderedBroadcasts.remove(0);
+ r = null;
+ looped = true;
+ continue;
+ }
+ } while (r == null);
+
+ // Get the next receiver...
+ int recIdx = r.nextReceiver++;
+
+ // Keep track of when this receiver started, and make sure there
+ // is a timeout message pending to kill it if need be.
+ r.receiverTime = SystemClock.uptimeMillis();
+ if (recIdx == 0) {
+ r.dispatchTime = r.receiverTime;
+ r.dispatchClockTime = System.currentTimeMillis();
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
+ }
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ + mQueueName + "] " + r);
+ }
+ if (! mPendingBroadcastTimeoutMessage) {
+ long timeoutTime = r.receiverTime + mTimeoutPeriod;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Submitting BROADCAST_TIMEOUT_MSG ["
+ + mQueueName + "] for " + r + " at " + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ }
+
+ final BroadcastOptions brOptions = r.options;
+ final Object nextReceiver = r.receivers.get(recIdx);
+
+ if (nextReceiver instanceof BroadcastFilter) {
+ // Simple case: this is a registered receiver who gets
+ // a direct call.
+ BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Delivering ordered ["
+ + mQueueName + "] to registered "
+ + filter + ": " + r);
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
+ if (r.receiver == null || !r.ordered) {
+ // The receiver has already finished, so schedule to
+ // process the next one.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ + mQueueName + "]: ordered="
+ + r.ordered + " receiver=" + r.receiver);
+ r.state = BroadcastRecord.IDLE;
+ scheduleBroadcastsLocked();
+ } else {
+ if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
+ scheduleTempWhitelistLocked(filter.owningUid,
+ brOptions.getTemporaryAppWhitelistDuration(), r);
+ }
+ }
+ return;
+ }
+
+ // Hard case: need to instantiate the receiver, possibly
+ // starting its application process to host it.
+
+ ResolveInfo info =
+ (ResolveInfo)nextReceiver;
+ ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
+
+ boolean skip = false;
+ if (brOptions != null &&
+ (info.activityInfo.applicationInfo.targetSdkVersion
+ < brOptions.getMinManifestReceiverApiLevel() ||
+ info.activityInfo.applicationInfo.targetSdkVersion
+ > brOptions.getMaxManifestReceiverApiLevel())) {
+ skip = true;
+ }
+ int perm = mService.checkComponentPermission(info.activityInfo.permission,
+ r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+ info.activityInfo.exported);
+ if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
+ if (!info.activityInfo.exported) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid
+ + " due to receiver " + component.flattenToShortString());
+ } else {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ")"
+ + " requires " + info.activityInfo.permission
+ + " due to receiver " + component.flattenToShortString());
+ }
+ skip = true;
+ } else if (!skip && info.activityInfo.permission != null) {
+ final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
+ if (opCode != AppOpsManager.OP_NONE
+ && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
+ r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(
+ info.activityInfo.permission)
+ + " due to registered receiver "
+ + component.flattenToShortString());
skip = true;
}
- int perm = mService.checkComponentPermission(info.activityInfo.permission,
- r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
- info.activityInfo.exported);
- if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
- if (!info.activityInfo.exported) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid
- + " due to receiver " + component.flattenToShortString());
- } else {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ")"
- + " requires " + info.activityInfo.permission
- + " due to receiver " + component.flattenToShortString());
+ }
+ if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+ r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ for (int i = 0; i < r.requiredPermissions.length; i++) {
+ String requiredPermission = r.requiredPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager().
+ checkPermission(requiredPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
}
- skip = true;
- } else if (!skip && info.activityInfo.permission != null) {
- final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
- if (opCode != AppOpsManager.OP_NONE
- && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
- r.callerPackage) != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires " + requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
+ && mService.mAppOpsService.noteOperation(appOp,
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ " requires appop " + AppOpsManager.permissionToOp(
- info.activityInfo.permission)
- + " due to registered receiver "
+ requiredPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ }
+ }
+ if (!skip && r.appOp != AppOpsManager.OP_NONE
+ && mService.mAppOpsService.noteOperation(r.appOp,
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires appop " + AppOpsManager.opToName(r.appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ if (!skip) {
+ skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
+ }
+ boolean isSingleton = false;
+ try {
+ isSingleton = mService.isSingleton(info.activityInfo.processName,
+ info.activityInfo.applicationInfo,
+ info.activityInfo.name, info.activityInfo.flags);
+ } catch (SecurityException e) {
+ Slog.w(TAG, e.getMessage());
+ skip = true;
+ }
+ if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ info.activityInfo.applicationInfo.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS);
+ skip = true;
+ }
+ }
+ if (!skip && info.activityInfo.applicationInfo.isInstantApp()
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " Instant Apps do not support manifest receivers");
+ skip = true;
+ }
+ if (!skip && r.callerInstantApp
+ && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " requires receiver have visibleToInstantApps set"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ if (r.curApp != null && r.curApp.crashing) {
+ // If the target process is crashing, just skip it.
+ Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
+ + " to " + r.curApp + ": process crashing");
+ skip = true;
+ }
+ if (!skip) {
+ boolean isAvailable = false;
+ try {
+ isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
+ info.activityInfo.packageName,
+ UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (Exception e) {
+ // all such failures mean we skip this receiver
+ Slog.w(TAG, "Exception getting recipient info for "
+ + info.activityInfo.packageName, e);
+ }
+ if (!isAvailable) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid
+ + " : package no longer available");
+ skip = true;
+ }
+ }
+
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (mService.mPermissionReviewRequired && !skip) {
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
+ info.activityInfo.packageName, UserHandle.getUserId(
+ info.activityInfo.applicationInfo.uid))) {
+ skip = true;
+ }
+ }
+
+ // This is safe to do even if we are skipping the broadcast, and we need
+ // this information now to evaluate whether it is going to be allowed to run.
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+ // If it's a singleton, it needs to be the same app or a special app
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton
+ && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
+ }
+ String targetProcess = info.activityInfo.processName;
+ ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+ info.activityInfo.applicationInfo.uid, false);
+
+ if (!skip) {
+ final int allowed = mService.getAppStartModeLocked(
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+ // We won't allow this receiver to be launched if the app has been
+ // completely disabled from launches, or it was not explicitly sent
+ // to it and the app is in a state that should not receive it
+ // (depending on how getAppStartModeLocked has determined that).
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Background execution disabled: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
+ || (r.intent.getComponent() == null
+ && r.intent.getPackage() == null
+ && ((r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
+ && !isSignaturePerm(r.requiredPermissions))) {
+ mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
+ component.getPackageName());
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ component.flattenToShortString());
skip = true;
}
}
- if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- try {
- perm = AppGlobals.getPackageManager().
- checkPermission(requiredPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.mAppOpsService.noteOperation(appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- if (!skip && r.appOp != AppOpsManager.OP_NONE
- && mService.mAppOpsService.noteOperation(r.appOp,
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.opToName(r.appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- if (!skip) {
- skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
- }
- boolean isSingleton = false;
- try {
- isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo,
- info.activityInfo.name, info.activityInfo.flags);
- } catch (SecurityException e) {
- Slog.w(TAG, e.getMessage());
- skip = true;
- }
- if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
- if (ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- info.activityInfo.applicationInfo.uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
- + " requests FLAG_SINGLE_USER, but app does not hold "
- + android.Manifest.permission.INTERACT_ACROSS_USERS);
- skip = true;
- }
- }
- if (!skip && info.activityInfo.applicationInfo.isInstantApp()
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")"
- + " Instant Apps do not support manifest receivers");
- skip = true;
- }
- if (!skip && r.callerInstantApp
- && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " requires receiver have visibleToInstantApps set"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- if (r.curApp != null && r.curApp.crashing) {
- // If the target process is crashing, just skip it.
- Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
- + " to " + r.curApp + ": process crashing");
- skip = true;
- }
- if (!skip) {
- boolean isAvailable = false;
- try {
- isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
- info.activityInfo.packageName,
- UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
- } catch (Exception e) {
- // all such failures mean we skip this receiver
- Slog.w(TAG, "Exception getting recipient info for "
- + info.activityInfo.packageName, e);
- }
- if (!isAvailable) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid
- + " : package no longer available");
- skip = true;
- }
- }
+ }
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (mService.mPermissionReviewRequired && !skip) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
- info.activityInfo.packageName, UserHandle.getUserId(
- info.activityInfo.applicationInfo.uid))) {
- skip = true;
- }
- }
+ if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
+ && !mService.mUserController
+ .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
+ 0 /* flags */)) {
+ skip = true;
+ Slog.w(TAG,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid + " : user is not running");
+ }
- // This is safe to do even if we are skipping the broadcast, and we need
- // this information now to evaluate whether it is going to be allowed to run.
- final int receiverUid = info.activityInfo.applicationInfo.uid;
- // If it's a singleton, it needs to be the same app or a special app
- if (r.callingUid != Process.SYSTEM_UID && isSingleton
- && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
- }
- String targetProcess = info.activityInfo.processName;
- ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid, false);
-
- if (!skip) {
- final int allowed = mService.getAppStartModeLocked(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
- if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
- // We won't allow this receiver to be launched if the app has been
- // completely disabled from launches, or it was not explicitly sent
- // to it and the app is in a state that should not receive it
- // (depending on how getAppStartModeLocked has determined that).
- if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Background execution disabled: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
- || (r.intent.getComponent() == null
- && r.intent.getPackage() == null
- && ((r.intent.getFlags()
- & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
- && !isSignaturePerm(r.requiredPermissions))) {
- mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
- component.getPackageName());
- Slog.w(TAG, "Background execution not allowed: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- }
- }
- }
-
- if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
- && !mService.mUserController
- .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
- 0 /* flags */)) {
- skip = true;
- Slog.w(TAG,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid + " : user is not running");
- }
-
- if (skip) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery of ordered [" + mQueueName + "] "
- + r + " for whatever reason");
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
- r.receiver = null;
- r.curFilter = null;
- r.state = BroadcastRecord.IDLE;
- r.manifestSkipCount++;
- scheduleBroadcastsLocked();
- return;
- }
- r.manifestCount++;
-
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
- r.state = BroadcastRecord.APP_RECEIVE;
- r.curComponent = component;
- r.curReceiver = info.activityInfo;
- if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
- Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
- + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
- + receiverUid);
- }
-
- if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
- scheduleTempWhitelistLocked(receiverUid,
- brOptions.getTemporaryAppWhitelistDuration(), r);
- }
-
- // Broadcast is being executed, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.curComponent.getPackageName() + ": " + e);
- }
-
- // Is this receiver's application already running?
- if (app != null && app.thread != null && !app.killed) {
- try {
- app.addPackage(info.activityInfo.packageName,
- info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
- processCurBroadcastLocked(r, app);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Failed sending broadcast to "
- + r.curComponent + " with " + r.intent, e);
- // If some unexpected exception happened, just skip
- // this broadcast. At this point we are not in the call
- // from a client, so throwing an exception out from here
- // will crash the entire system instead of just whoever
- // sent the broadcast.
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- // Not running -- get it started, to be executed when the app comes up.
+ if (skip) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Need to start app ["
- + mQueueName + "] " + targetProcess + " for broadcast " + r);
- if ((r.curApp=mService.startProcessLocked(targetProcess,
- info.activityInfo.applicationInfo, true,
- r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- "broadcast", r.curComponent,
- (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
- == null) {
- // Ah, this recipient is unavailable. Finish it if necessary,
- // and mark the broadcast record as ready for the next.
- Slog.w(TAG, "Unable to launch app "
- + info.activityInfo.applicationInfo.packageName + "/"
- + receiverUid + " for broadcast "
- + r.intent + ": process is bad");
+ "Skipping delivery of ordered [" + mQueueName + "] "
+ + r + " for whatever reason");
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
+ r.receiver = null;
+ r.curFilter = null;
+ r.state = BroadcastRecord.IDLE;
+ r.manifestSkipCount++;
+ scheduleBroadcastsLocked();
+ return;
+ }
+ r.manifestCount++;
+
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
+ r.state = BroadcastRecord.APP_RECEIVE;
+ r.curComponent = component;
+ r.curReceiver = info.activityInfo;
+ if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + receiverUid);
+ }
+
+ if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
+ scheduleTempWhitelistLocked(receiverUid,
+ brOptions.getTemporaryAppWhitelistDuration(), r);
+ }
+
+ // Broadcast is being executed, its package can't be stopped.
+ try {
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + r.curComponent.getPackageName() + ": " + e);
+ }
+
+ // Is this receiver's application already running?
+ if (app != null && app.thread != null && !app.killed) {
+ try {
+ app.addPackage(info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
+ processCurBroadcastLocked(r, app, skipOomAdj);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when sending broadcast to "
+ + r.curComponent, e);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Failed sending broadcast to "
+ + r.curComponent + " with " + r.intent, e);
+ // If some unexpected exception happened, just skip
+ // this broadcast. At this point we are not in the call
+ // from a client, so throwing an exception out from here
+ // will crash the entire system instead of just whoever
+ // sent the broadcast.
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
+ // We need to reset the state if we failed to start the receiver.
r.state = BroadcastRecord.IDLE;
return;
}
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
}
+
+ // Not running -- get it started, to be executed when the app comes up.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Need to start app ["
+ + mQueueName + "] " + targetProcess + " for broadcast " + r);
+ if ((r.curApp=mService.startProcessLocked(targetProcess,
+ info.activityInfo.applicationInfo, true,
+ r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+ "broadcast", r.curComponent,
+ (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
+ == null) {
+ // Ah, this recipient is unavailable. Finish it if necessary,
+ // and mark the broadcast record as ready for the next.
+ Slog.w(TAG, "Unable to launch app "
+ + info.activityInfo.applicationInfo.packageName + "/"
+ + receiverUid + " for broadcast "
+ + r.intent + ": process is bad");
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ mPendingBroadcast = r;
+ mPendingBroadcastRecvIndex = recIdx;
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index a3d2173..67230c3 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -169,6 +169,7 @@
.setMayWait(mUserController.getCurrentUserId())
.execute();
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+ mWindowManager.executeAppTransition();
targetActivity = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType).getTopActivity();
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 32887e4..4d89d01 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -73,7 +73,7 @@
// original intent used to find service.
final ServiceInfo serviceInfo;
// all information about the service.
- final ApplicationInfo appInfo;
+ ApplicationInfo appInfo;
// information about service's app.
final int userId; // user that this service is running as
final String packageName; // the package implementing intent's component
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3d25325..6c79c1a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -254,6 +254,7 @@
private static final int MSG_ACCESSORY_PLUG_MEDIA_UNMUTE = 27;
private static final int MSG_NOTIFY_VOL_EVENT = 28;
private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 29;
+ private static final int MSG_ENABLE_SURROUND_FORMATS = 30;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -633,6 +634,10 @@
private int[] mAccessibilityServiceUids;
private final Object mAccessibilityServiceUidsLock = new Object();
+ private int mEncodedSurroundMode;
+ private String mEnabledSurroundFormats;
+ private boolean mSurroundModeChanged;
+
// Intent "extra" data keys.
public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1035,6 +1040,7 @@
"onAudioServerDied"));
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock);
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
+ sendEnabledSurroundFormats(mContentResolver, true);
}
if (mHdmiManager != null) {
synchronized (mHdmiManager) {
@@ -1288,6 +1294,9 @@
case Settings.Global.ENCODED_SURROUND_OUTPUT_ALWAYS:
forceSetting = AudioSystem.FORCE_ENCODED_SURROUND_ALWAYS;
break;
+ case Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL:
+ forceSetting = AudioSystem.FORCE_ENCODED_SURROUND_MANUAL;
+ break;
default:
Log.e(TAG, "updateSurroundSoundSettings: illegal value "
+ encodedSurroundMode);
@@ -1304,6 +1313,56 @@
}
}
+ private void sendEnabledSurroundFormats(ContentResolver cr, boolean forceUpdate) {
+ if (mEncodedSurroundMode != Settings.Global.ENCODED_SURROUND_OUTPUT_MANUAL) {
+ // Manually enable surround formats only when the setting is in manual mode.
+ return;
+ }
+ String enabledSurroundFormats = Settings.Global.getString(
+ cr, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
+ if (!forceUpdate && TextUtils.equals(enabledSurroundFormats, mEnabledSurroundFormats)) {
+ // Update enabled surround formats to AudioPolicyManager only when forceUpdate
+ // is true or enabled surround formats changed.
+ return;
+ }
+
+ mEnabledSurroundFormats = enabledSurroundFormats;
+ String[] surroundFormats = TextUtils.split(enabledSurroundFormats, ",");
+ ArrayList<Integer> formats = new ArrayList<>();
+ for (String format : surroundFormats) {
+ try {
+ int audioFormat = Integer.valueOf(format);
+ boolean isSurroundFormat = false;
+ for (int sf : AudioFormat.SURROUND_SOUND_ENCODING) {
+ if (sf == audioFormat) {
+ isSurroundFormat = true;
+ break;
+ }
+ }
+ if (isSurroundFormat && !formats.contains(audioFormat)) {
+ formats.add(audioFormat);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid enabled surround format:" + format);
+ }
+ }
+ // Set filtered surround formats to settings DB in case
+ // there are invalid surround formats in original settings.
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS,
+ TextUtils.join(",", formats));
+ sendMsg(mAudioHandler, MSG_ENABLE_SURROUND_FORMATS, SENDMSG_QUEUE, 0, 0, formats, 0);
+ }
+
+ private void onEnableSurroundFormats(ArrayList<Integer> enabledSurroundFormats) {
+ // Set surround format enabled accordingly.
+ for (int surroundFormat : AudioFormat.SURROUND_SOUND_ENCODING) {
+ boolean enabled = enabledSurroundFormats.contains(surroundFormat);
+ int ret = AudioSystem.setSurroundFormatEnabled(surroundFormat, enabled);
+ Log.i(TAG, "enable surround format:" + surroundFormat + " " + enabled + " " + ret);
+ }
+ }
+
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
@@ -1346,6 +1405,7 @@
updateRingerAndZenModeAffectedStreams();
readDockAudioSettings(cr);
sendEncodedSurroundMode(cr, "readPersistedSettings");
+ sendEnabledSurroundFormats(cr, true);
}
mMuteAffectedStreams = System.getIntForUser(cr,
@@ -3653,26 +3713,35 @@
}
String address = btDevice.getAddress();
BluetoothClass btClass = btDevice.getBluetoothClass();
- int outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+ int[] outDeviceTypes = {
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+ AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
+ };
if (btClass != null) {
switch (btClass.getDeviceClass()) {
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
break;
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- outDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
break;
}
}
-
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
-
String btDeviceName = btDevice.getName();
- boolean result = handleDeviceConnection(isActive, outDevice, address, btDeviceName);
+ boolean result = false;
+ if (isActive) {
+ result |= handleDeviceConnection(isActive, outDeviceTypes[0], address, btDeviceName);
+ } else {
+ for (int outDeviceType : outDeviceTypes) {
+ result |= handleDeviceConnection(isActive, outDeviceType, address, btDeviceName);
+ }
+ }
// handleDeviceConnection() && result to make sure the method get executed
result = handleDeviceConnection(isActive, inDevice, address, btDeviceName) && result;
return result;
@@ -5623,14 +5692,16 @@
case MSG_NOTIFY_VOL_EVENT:
onNotifyVolumeEvent((IAudioPolicyCallback) msg.obj, msg.arg1);
break;
+
+ case MSG_ENABLE_SURROUND_FORMATS:
+ onEnableSurroundFormats((ArrayList<Integer>) msg.obj);
+ break;
}
}
}
private class SettingsObserver extends ContentObserver {
- private int mEncodedSurroundMode;
-
SettingsObserver() {
super(new Handler());
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
@@ -5649,6 +5720,11 @@
Settings.Global.ENCODED_SURROUND_OUTPUT_AUTO);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.ENCODED_SURROUND_OUTPUT), false, this);
+
+ mEnabledSurroundFormats = Settings.Global.getString(
+ mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
}
@Override
@@ -5669,6 +5745,7 @@
readDockAudioSettings(mContentResolver);
updateMasterMono(mContentResolver);
updateEncodedSurroundOutput();
+ sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
}
}
@@ -5695,6 +5772,9 @@
}
}
mEncodedSurroundMode = newSurroundMode;
+ mSurroundModeChanged = true;
+ } else {
+ mSurroundModeChanged = false;
}
}
}
@@ -6231,6 +6311,9 @@
}
}
}
+ if ((device & AudioSystem.DEVICE_OUT_HDMI) != 0) {
+ sendEnabledSurroundFormats(mContentResolver, true);
+ }
} else {
if (isPlatformTelevision() && ((device & AudioSystem.DEVICE_OUT_HDMI) != 0)) {
if (mHdmiManager != null) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index ce25a9f..1c3342a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -20,12 +20,14 @@
import com.android.server.LocalServices;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -91,6 +93,8 @@
// The minimum and maximum screen brightnesses.
private final int mScreenBrightnessRangeMinimum;
private final int mScreenBrightnessRangeMaximum;
+
+ // How much to scale doze brightness by (should be (0, 1.0]).
private final float mDozeScaleFactor;
// Initial light sensor event rate in milliseconds.
@@ -122,8 +126,8 @@
// weighting values positive.
private final int mWeightingIntercept;
- // accessor object for determining thresholds to change brightness dynamically
- private final HysteresisLevels mDynamicHysteresis;
+ // Configuration object for determining thresholds to change brightness dynamically
+ private final HysteresisLevels mHysteresisLevels;
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
@@ -192,7 +196,7 @@
int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
- int ambientLightHorizon, HysteresisLevels dynamicHysteresis) {
+ HysteresisLevels hysteresisLevels) {
mCallbacks = callbacks;
mSensorManager = sensorManager;
mBrightnessMapper = mapper;
@@ -206,9 +210,9 @@
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
- mAmbientLightHorizon = ambientLightHorizon;
- mWeightingIntercept = ambientLightHorizon;
- mDynamicHysteresis = dynamicHysteresis;
+ mAmbientLightHorizon = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+ mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
+ mHysteresisLevels = hysteresisLevels;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
@@ -342,19 +346,24 @@
pw.println("Automatic Brightness Controller Configuration:");
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ pw.println(" mDozeScaleFactor=" + mDozeScaleFactor);
+ pw.println(" mInitialLightSensorRate=" + mInitialLightSensorRate);
+ pw.println(" mNormalLightSensorRate=" + mNormalLightSensorRate);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
+ pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
+ pw.println(" mWeightingIntercept=" + mWeightingIntercept);
pw.println();
pw.println("Automatic Brightness Controller State:");
pw.println(" mLightSensor=" + mLightSensor);
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+ pw.println(" mCurrentLightSensorRate=" + mCurrentLightSensorRate);
pw.println(" mAmbientLux=" + mAmbientLux);
pw.println(" mAmbientLuxValid=" + mAmbientLuxValid);
- pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
pw.println(" mLastObservedLux=" + mLastObservedLux);
@@ -362,11 +371,20 @@
pw.println(" mRecentLightSamples=" + mRecentLightSamples);
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
- pw.println(" mDisplayPolicy=" + mDisplayPolicy);
+ pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
+ pw.println(" mShortTermModelValid=" + mShortTermModelValid);
+ pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
+ pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
+ pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ + mBrightnessAdjustmentSampleOldBrightness);
+ pw.println(" mShortTermModelValid=" + mShortTermModelValid);
pw.println();
mBrightnessMapper.dump(pw);
+
+ pw.println();
+ mHysteresisLevels.dump(pw);
}
private boolean setLightSensorEnabled(boolean enable) {
@@ -441,8 +459,8 @@
lux = 0;
}
mAmbientLux = lux;
- mBrighteningLuxThreshold = mDynamicHysteresis.getBrighteningThreshold(lux);
- mDarkeningLuxThreshold = mDynamicHysteresis.getDarkeningThreshold(lux);
+ mBrighteningLuxThreshold = mHysteresisLevels.getBrighteningThreshold(lux);
+ mDarkeningLuxThreshold = mHysteresisLevels.getDarkeningThreshold(lux);
// If the short term model was invalidated and the change is drastic enough, reset it.
if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 5935829..1b688a6 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -42,7 +42,7 @@
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 407fad0..a568fe3 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -68,10 +68,11 @@
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
-
import java.util.Collections;
+import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
@@ -115,6 +116,8 @@
private static final int MSG_STOP_SENSOR_LISTENER = 2;
private static final int MSG_START_SENSOR_LISTENER = 3;
+ private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
// Lock held while accessing mEvents, is held while writing events to flash.
private final Object mEventsLock = new Object();
@GuardedBy("mEventsLock")
@@ -127,10 +130,10 @@
private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
- private UserManager mUserManager;
+ private final UserManager mUserManager;
private final Context mContext;
private final ContentResolver mContentResolver;
- private Handler mBgHandler;
+ private final Handler mBgHandler;
// mBroadcastReceiver, mSensorListener, mSettingsObserver and mSensorRegistered
// should only be used on the mBgHandler thread.
@@ -164,6 +167,8 @@
} else {
mInjector = new Injector();
}
+ mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
+ mUserManager = mContext.getSystemService(UserManager.class);
}
/**
@@ -175,8 +180,6 @@
if (DEBUG) {
Slog.d(TAG, "Start");
}
- mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
- mUserManager = mContext.getSystemService(UserManager.class);
mCurrentUserId = ActivityManager.getCurrentUser();
mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
}
@@ -649,10 +652,12 @@
}
}
- public void dump(PrintWriter pw) {
+ public void dump(final PrintWriter pw) {
pw.println("BrightnessTracker state:");
synchronized (mDataCollectionLock) {
pw.println(" mStarted=" + mStarted);
+ pw.println(" mLastBatteryLevel=" + mLastBatteryLevel);
+ pw.println(" mLastBrightness=" + mLastBrightness);
pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
if (!mLastSensorReadings.isEmpty()) {
pw.println(" mLastSensorReadings time span "
@@ -665,7 +670,8 @@
pw.println(" mEvents.size=" + mEvents.size());
BrightnessChangeEvent[] events = mEvents.toArray();
for (int i = 0; i < events.length; ++i) {
- pw.print(" " + events[i].timeStamp + ", " + events[i].userId);
+ pw.print(" " + FORMAT.format(new Date(events[i].timeStamp)));
+ pw.print(", userId=" + events[i].userId);
pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
@@ -680,17 +686,23 @@
pw.println("}");
}
}
+ pw.println(" mWriteBrightnessTrackerStateScheduled="
+ + mWriteBrightnessTrackerStateScheduled);
+ mBgHandler.runWithScissors(() -> dumpLocal(pw), 1000);
if (mAmbientBrightnessStatsTracker != null) {
pw.println();
mAmbientBrightnessStatsTracker.dump(pw);
}
}
+ private void dumpLocal(PrintWriter pw) {
+ pw.println(" mSensorRegistered=" + mSensorRegistered);
+ }
+
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
if (mAmbientBrightnessStatsTracker != null) {
ArrayList<AmbientBrightnessDayStats> stats =
- mAmbientBrightnessStatsTracker.getUserStats(
- userId);
+ mAmbientBrightnessStatsTracker.getUserStats(userId);
if (stats != null) {
return new ParceledListSlice<>(stats);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3b35d02..1be507e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -295,6 +295,10 @@
private boolean mAppliedAutoBrightness;
private boolean mAppliedDimming;
private boolean mAppliedLowPower;
+ private boolean mAppliedScreenBrightnessOverride;
+ private boolean mAppliedTemporaryBrightness;
+ private boolean mAppliedTemporaryAutoBrightnessAdjustment;
+ private boolean mAppliedBrightnessBoost;
// Brightness animation ramp rates in brightness units per second
private final int mBrightnessRampRateFast;
@@ -424,7 +428,7 @@
com.android.internal.R.array.config_dynamicHysteresisDarkLevels);
int[] luxHysteresisLevels = resources.getIntArray(
com.android.internal.R.array.config_dynamicHysteresisLuxLevels);
- HysteresisLevels dynamicHysteresis = new HysteresisLevels(
+ HysteresisLevels hysteresisLevels = new HysteresisLevels(
brightLevels, darkLevels, luxHysteresisLevels);
long brighteningLightDebounce = resources.getInteger(
@@ -433,8 +437,6 @@
com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
- int ambientLightHorizon = resources.getInteger(
- com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
int lightSensorWarmUpTimeConfig = resources.getInteger(
com.android.internal.R.integer.config_lightSensorWarmupTime);
@@ -457,8 +459,7 @@
lightSensorWarmUpTimeConfig, mScreenBrightnessRangeMinimum,
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp, ambientLightHorizon,
- dynamicHysteresis);
+ autoBrightnessResetAmbientLuxAfterWarmUp, hysteresisLevels);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -776,6 +777,9 @@
if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
brightness = mPowerRequest.screenBrightnessOverride;
+ mAppliedScreenBrightnessOverride = true;
+ } else {
+ mAppliedScreenBrightnessOverride = false;
}
final boolean autoBrightnessEnabledInDoze =
@@ -784,7 +788,6 @@
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightness < 0
&& mAutomaticBrightnessController != null;
- boolean brightnessIsTemporary = false;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
if (userSetBrightnessChanged) {
@@ -795,7 +798,9 @@
// WindowManager or based on the display state.
if (mTemporaryScreenBrightness > 0) {
brightness = mTemporaryScreenBrightness;
- brightnessIsTemporary = true;
+ mAppliedTemporaryBrightness = true;
+ } else {
+ mAppliedTemporaryBrightness = false;
}
final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
@@ -807,9 +812,10 @@
final float autoBrightnessAdjustment;
if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
- brightnessIsTemporary = true;
+ mAppliedTemporaryAutoBrightnessAdjustment = true;
} else {
autoBrightnessAdjustment = mAutoBrightnessAdjustment;
+ mAppliedTemporaryAutoBrightnessAdjustment = true;
}
// Apply brightness boost.
@@ -819,6 +825,9 @@
if (mPowerRequest.boostScreenBrightness
&& brightness != PowerManager.BRIGHTNESS_OFF) {
brightness = PowerManager.BRIGHTNESS_ON;
+ mAppliedBrightnessBoost = true;
+ } else {
+ mAppliedBrightnessBoost = false;
}
// If the brightness is already set then it's been overridden by something other than the
@@ -948,6 +957,8 @@
// level without it being a noticeable jump since any actual content isn't yet visible.
final boolean isDisplayContentVisible =
mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
+ final boolean brightnessIsTemporary =
+ mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
if (initialRampSkip || hasBrightnessBuckets
|| wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
animateScreenBrightness(brightness, 0);
@@ -1586,10 +1597,20 @@
pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+ pw.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault);
+ pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
+ pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
+ pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
pw.println(" mAllowAutoBrightnessWhileDozingConfig=" +
mAllowAutoBrightnessWhileDozingConfig);
+ pw.println(" mBrightnessRampRateFast=" + mBrightnessRampRateFast);
+ pw.println(" mBrightnessRampRateSlow=" + mBrightnessRampRateSlow);
+ pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
+ pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
+ pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
+ pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
mHandler.runWithScissors(new Runnable() {
@Override
@@ -1603,6 +1624,7 @@
pw.println();
pw.println("Display Power Controller Thread State:");
pw.println(" mPowerRequest=" + mPowerRequest);
+ pw.println(" mUnfinishedBusiness=" + mUnfinishedBusiness);
pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
pw.println(" mProximitySensor=" + mProximitySensor);
pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
@@ -1615,12 +1637,23 @@
pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
pw.println(" mCurrentScreenBrightnessSetting=" + mCurrentScreenBrightnessSetting);
pw.println(" mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting);
+ pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
+ pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
+ pw.println(" mScreenBrightnessForVr=" + mScreenBrightnessForVr);
pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness);
pw.println(" mAppliedDimming=" + mAppliedDimming);
pw.println(" mAppliedLowPower=" + mAppliedLowPower);
+ pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
+ pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
+ pw.println(" mDozing=" + mDozing);
+ pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState));
+ pw.println(" mInitialAutoBrightness=" + mInitialAutoBrightness);
+ pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
+ pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
+ pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
pw.println(" mPendingScreenOff=" + mPendingScreenOff);
pw.println(" mReportedToPolicy=" +
reportedToPolicyToString(mReportedScreenStateToPolicy));
@@ -1677,6 +1710,19 @@
}
}
+ private static String skipRampStateToString(int state) {
+ switch (state) {
+ case RAMP_STATE_SKIP_NONE:
+ return "RAMP_STATE_SKIP_NONE";
+ case RAMP_STATE_SKIP_INITIAL:
+ return "RAMP_STATE_SKIP_INITIAL";
+ case RAMP_STATE_SKIP_AUTOBRIGHT:
+ return "RAMP_STATE_SKIP_AUTOBRIGHT";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
private static int clampAbsoluteBrightness(int value) {
return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
}
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index b062225..1c02dd1 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,6 +18,9 @@
import android.util.Slog;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
/**
* A helper class for handling access to illuminance hysteresis level values.
*/
@@ -101,4 +104,11 @@
}
return levelArray;
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("HysteresisLevels");
+ pw.println(" mBrightLevels=" + Arrays.toString(mBrightLevels));
+ pw.println(" mDarkLevels=" + Arrays.toString(mDarkLevels));
+ pw.println(" mLuxLevels=" + Arrays.toString(mLuxLevels));
+ }
}
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index 8b8faa3..e28e5bd 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -58,14 +58,17 @@
private final int[] mEventUids = new int[EVENT_BUFFER_SIZE];
private final String[] mEventTags = new String[EVENT_BUFFER_SIZE];
private final int[] mEventJobIds = new int[EVENT_BUFFER_SIZE];
+ private final String[] mEventReasons = new String[EVENT_BUFFER_SIZE];
- public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason) {
+ public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason,
+ String debugReason) {
int index = mEventIndices.add();
mEventCmds[index] = cmd | ((stopReason<<EVENT_STOP_REASON_SHIFT) & EVENT_STOP_REASON_MASK);
mEventTimes[index] = sElapsedRealtimeClock.millis();
mEventUids[index] = uid;
mEventTags[index] = tag;
mEventJobIds[index] = jobId;
+ mEventReasons[index] = debugReason;
}
DataSet mCurDataSet = new DataSet();
@@ -470,10 +473,10 @@
mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now);
}
addEvent(job.getJob().isPeriodic() ? EVENT_START_PERIODIC_JOB : EVENT_START_JOB,
- job.getSourceUid(), job.getBatteryName(), job.getJobId(), 0);
+ job.getSourceUid(), job.getBatteryName(), job.getJobId(), 0, null);
}
- public void noteInactive(JobStatus job, int stopReason) {
+ public void noteInactive(JobStatus job, int stopReason, String debugReason) {
final long now = sUptimeMillisClock.millis();
if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now,
@@ -483,7 +486,7 @@
}
rebatchIfNeeded(now);
addEvent(job.getJob().isPeriodic() ? EVENT_STOP_JOB : EVENT_STOP_PERIODIC_JOB,
- job.getSourceUid(), job.getBatteryName(), job.getJobId(), stopReason);
+ job.getSourceUid(), job.getBatteryName(), job.getJobId(), stopReason, debugReason);
}
public void noteConcurrency(int totalActive, int fgActive) {
@@ -599,8 +602,13 @@
pw.print(mEventTags[index]);
if (cmd == EVENT_STOP_JOB || cmd == EVENT_STOP_PERIODIC_JOB) {
pw.print(" ");
- pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK)
- >> EVENT_STOP_REASON_SHIFT));
+ final String reason = mEventReasons[index];
+ if (reason != null) {
+ pw.print(mEventReasons[index]);
+ } else {
+ pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK)
+ >> EVENT_STOP_REASON_SHIFT));
+ }
}
pw.println();
}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 0bb6854..8578bb7 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -770,7 +770,7 @@
}
applyStoppedReasonLocked(reason);
completedJob = mRunningJob;
- mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason());
+ mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
mRunningJob.getSourceUid(), mParams.getStopReason());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bf17798..e2e6f6d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -243,6 +243,9 @@
public static final boolean ENABLE_CHILD_NOTIFICATIONS
= SystemProperties.getBoolean("debug.child_notifs", true);
+ static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
+ "debug.notification.interruptiveness", false);
+
static final int MAX_PACKAGE_NOTIFICATIONS = 50;
static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
@@ -4437,7 +4440,7 @@
if (index < 0) {
mNotificationList.add(r);
mUsageStats.registerPostedByApp(r);
- r.setInterruptive(true);
+ r.setInterruptive(isVisuallyInterruptive(null, r));
} else {
old = mNotificationList.get(index);
mNotificationList.set(index, r);
@@ -4516,31 +4519,75 @@
@GuardedBy("mNotificationLock")
@VisibleForTesting
protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
+ if (old == null) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: new notification");
+ }
+ return true;
+ }
+
Notification oldN = old.sbn.getNotification();
Notification newN = r.sbn.getNotification();
+
if (oldN.extras == null || newN.extras == null) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: no extras");
+ }
return false;
}
// Ignore visual interruptions from foreground services because users
// consider them one 'session'. Count them for everything else.
if (r != null && (r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: foreground service");
+ }
return false;
}
- if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TITLE),
- newN.extras.get(Notification.EXTRA_TITLE))) {
+ final String oldTitle = String.valueOf(oldN.extras.get(Notification.EXTRA_TITLE));
+ final String newTitle = String.valueOf(newN.extras.get(Notification.EXTRA_TITLE));
+ if (!Objects.equals(oldTitle, newTitle)) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: changed title");
+ Log.v(TAG, "INTERRUPTIVENESS: " + String.format(" old title: %s (%s@0x%08x)",
+ oldTitle, oldTitle.getClass(), oldTitle.hashCode()));
+ Log.v(TAG, "INTERRUPTIVENESS: " + String.format(" new title: %s (%s@0x%08x)",
+ newTitle, newTitle.getClass(), newTitle.hashCode()));
+ }
return true;
}
- if (!Objects.equals(oldN.extras.get(Notification.EXTRA_TEXT),
- newN.extras.get(Notification.EXTRA_TEXT))) {
+ // Do not compare Spannables (will always return false); compare unstyled Strings
+ final String oldText = String.valueOf(oldN.extras.get(Notification.EXTRA_TEXT));
+ final String newText = String.valueOf(newN.extras.get(Notification.EXTRA_TEXT));
+ if (!Objects.equals(oldText, newText)) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: changed text");
+ Log.v(TAG, "INTERRUPTIVENESS: " + String.format(" old text: %s (%s@0x%08x)",
+ oldText, oldText.getClass(), oldText.hashCode()));
+ Log.v(TAG, "INTERRUPTIVENESS: " + String.format(" new text: %s (%s@0x%08x)",
+ newText, newText.getClass(), newText.hashCode()));
+ }
return true;
}
- if (oldN.extras.containsKey(Notification.EXTRA_PROGRESS) && newN.hasCompletedProgress()) {
+ if (oldN.hasCompletedProgress() != newN.hasCompletedProgress()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: completed progress");
+ }
return true;
}
// Actions
if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: changed actions");
+ }
return true;
}
@@ -4550,16 +4597,25 @@
// Style based comparisons
if (Notification.areStyledNotificationsVisiblyDifferent(oldB, newB)) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: styles differ");
+ }
return true;
}
// Remote views
if (Notification.areRemoteViewsChanged(oldB, newB)) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Log.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is interruptive: remoteviews differ");
+ }
return true;
}
} catch (Exception e) {
Slog.w(TAG, "error recovering builder", e);
}
+
return false;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 16440c8..5ee7084 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -267,6 +267,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyguardDismissCallback;
@@ -621,6 +622,7 @@
boolean mUseTvRouting;
int mVeryLongPressTimeout;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
+ MetricsLogger mLogger;
private boolean mHandleVolumeKeysInWM;
@@ -1136,6 +1138,7 @@
}
getAudioManagerInternal();
mAudioManagerInternal.silenceRingerModeInternal("volume_hush");
+ mLogger.action(MetricsProto.MetricsEvent.ACTION_HUSH_GESTURE, mRingerToggleChord);
}
IStatusBarService getStatusBarService() {
@@ -2003,6 +2006,7 @@
mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
mAccessibilityShortcutController =
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
+ mLogger = new MetricsLogger();
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_enableBurnInProtection);
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 507f0a8..2621c50 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -36,6 +36,7 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -49,6 +50,8 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
@@ -69,6 +72,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -320,6 +324,12 @@
}
}
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ new SliceShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
/// ----- internal code -----
private void enforceOwner(String pkg, Uri uri, int user) {
if (!Objects.equals(getProviderPkg(uri, user), pkg) || pkg == null) {
@@ -541,6 +551,14 @@
}
};
+ public String[] getAllPackagesGranted(String authority) {
+ String pkg = getProviderPkg(new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .build(), 0);
+ return mPermissions.getAllPackagesGranted(pkg);
+ }
+
public static class Lifecycle extends SystemService {
private SliceManagerService mService;
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
index c67f94b..780bc96 100644
--- a/services/core/java/com/android/server/slice/SlicePermissionManager.java
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -133,6 +133,16 @@
mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
}
+ public String[] getAllPackagesGranted(String pkg) {
+ ArraySet<String> ret = new ArraySet<>();
+ for (SliceAuthority authority : getProvider(new PkgUser(pkg, 0)).getAuthorities()) {
+ for (PkgUser pkgUser : authority.getPkgs()) {
+ ret.add(pkgUser.mPkg);
+ }
+ }
+ return ret.toArray(new String[ret.size()]);
+ }
+
public boolean hasFullAccess(String pkg, int userId) {
PkgUser pkgUser = new PkgUser(pkg, userId);
return getClient(pkgUser).hasFullAccess();
diff --git a/services/core/java/com/android/server/slice/SliceShellCommand.java b/services/core/java/com/android/server/slice/SliceShellCommand.java
new file mode 100644
index 0000000..9137a3b
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceShellCommand.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import android.app.slice.SliceProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.ShellCommand;
+import android.util.ArraySet;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Set;
+
+public class SliceShellCommand extends ShellCommand {
+
+ private final SliceManagerService mService;
+
+ public SliceShellCommand(SliceManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "get-permissions":
+ return runGetPermissions(getNextArgRequired());
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Status bar commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" get-permissions <authority>");
+ pw.println(" List the pkgs that have permission to an authority.");
+ pw.println("");
+
+ }
+
+ private int runGetPermissions(String authority) {
+ if (Binder.getCallingUid() != Process.SHELL_UID
+ && Binder.getCallingUid() != Process.ROOT_UID) {
+ getOutPrintWriter().println("Only shell can get permissions");
+ return -1;
+ }
+ Context context = mService.getContext();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ Uri uri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .build();
+ if (!SliceProvider.SLICE_TYPE.equals(context.getContentResolver().getType(uri))) {
+ getOutPrintWriter().println(authority + " is not a slice provider");
+ return -1;
+ }
+ Bundle b = context.getContentResolver().call(uri, SliceProvider.METHOD_GET_PERMISSIONS,
+ null, null);
+ if (b == null) {
+ getOutPrintWriter().println("An error occurred getting permissions");
+ return -1;
+ }
+ String[] permissions = b.getStringArray(SliceProvider.EXTRA_RESULT);
+ final PrintWriter pw = getOutPrintWriter();
+ Set<String> listedPackages = new ArraySet<>();
+ if (permissions != null && permissions.length != 0) {
+ List<PackageInfo> apps =
+ context.getPackageManager().getPackagesHoldingPermissions(permissions, 0);
+ for (PackageInfo app : apps) {
+ pw.println(app.packageName);
+ listedPackages.add(app.packageName);
+ }
+ }
+ for (String pkg : mService.getAllPackagesGranted(authority)) {
+ if (!listedPackages.contains(pkg)) {
+ pw.println(pkg);
+ listedPackages.add(pkg);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 891ee2e..2fbb846 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -800,34 +800,7 @@
}
private void updateBoundsForWindowModeChange() {
- Rect bounds = null;
- final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
- final TaskStack splitScreenStack =
- mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
- if (inSplitScreenPrimary || (splitScreenStack != null
- && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) {
- // The existence of a docked stack affects the size of other static stack created since
- // the docked stack occupies a dedicated region on screen, but only if the dock stack is
- // not fullscreen. If it's fullscreen, it means that we are in the transition of
- // dismissing it, so we must not resize this stack.
- bounds = new Rect();
- mDisplayContent.getBounds(mTmpRect);
- mTmpRect2.setEmpty();
- if (splitScreenStack != null) {
- splitScreenStack.getRawBounds(mTmpRect2);
- }
- final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
- == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
- mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
- } else if (inPinnedWindowingMode()) {
- // Update the bounds based on any changes to the display info
- getAnimationOrCurrentBounds(mTmpRect2);
- if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
- mTmpRect2, mTmpRect3)) {
- bounds = new Rect(mTmpRect3);
- }
- }
+ final Rect bounds = calculateBoundsForWindowModeChange();
if (inSplitScreenSecondaryWindowingMode()) {
// When the stack is resized due to entering split screen secondary, offset the
@@ -841,6 +814,49 @@
updateSurfaceBounds();
}
+ private Rect calculateBoundsForWindowModeChange() {
+ final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
+ final TaskStack splitScreenStack =
+ mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ if (inSplitScreenPrimary || (splitScreenStack != null
+ && inSplitScreenSecondaryWindowingMode() && !splitScreenStack.fillsParent())) {
+ // The existence of a docked stack affects the size of other static stack created since
+ // the docked stack occupies a dedicated region on screen, but only if the dock stack is
+ // not fullscreen. If it's fullscreen, it means that we are in the transition of
+ // dismissing it, so we must not resize this stack.
+ final Rect bounds = new Rect();
+ mDisplayContent.getBounds(mTmpRect);
+ mTmpRect2.setEmpty();
+ if (splitScreenStack != null) {
+ if (inSplitScreenSecondaryWindowingMode()
+ && mDisplayContent.mDividerControllerLocked.isMinimizedDock()
+ && splitScreenStack.getTopChild() != null) {
+ // If the primary split screen stack is currently minimized, then don't use the
+ // stack bounds of the minimized stack, instead, use the temporary task bounds
+ // to calculate the appropriate uniminized size of any secondary split stack
+ // TODO: Find a cleaner way for computing new stack bounds while minimized that
+ // doesn't assume the primary stack's task bounds as the temp task bounds
+ splitScreenStack.getTopChild().getBounds(mTmpRect2);
+ } else {
+ splitScreenStack.getRawBounds(mTmpRect2);
+ }
+ }
+ final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
+ == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+ getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
+ mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
+ return bounds;
+ } else if (inPinnedWindowingMode()) {
+ // Update the bounds based on any changes to the display info
+ getAnimationOrCurrentBounds(mTmpRect2);
+ if (mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
+ mTmpRect2, mTmpRect3)) {
+ return new Rect(mTmpRect3);
+ }
+ }
+ return null;
+ }
+
/**
* Determines the stack and task bounds of the other stack when in docked mode. The current task
* bounds is passed in but depending on the stack, the task and stack must match. Only in
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 10dfdf2..16399008 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1797,6 +1797,12 @@
}
w.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
+
+ // We need to report touchable region changes to accessibility.
+ if (mAccessibilityController != null
+ && w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+ mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
}
}
} finally {
@@ -2691,12 +2697,7 @@
+ " Callers=" + Debug.getCallers(5));
if (mAppTransition.isTransitionSet()) {
mAppTransition.setReady();
- final long origId = Binder.clearCallingIdentity();
- try {
- mWindowPlacerLocked.performSurfacePlacement();
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ mWindowPlacerLocked.requestTraversal();
}
}
}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 83a125d..dc55179 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -18,8 +18,12 @@
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_INSTANT;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SHELL_UID;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ComponentName;
@@ -36,6 +40,8 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.print.IPrintDocumentAdapter;
@@ -122,6 +128,13 @@
}
@Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ new PrintShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
PrintAttributes attributes, String packageName, int appId, int userId) {
adapter = Preconditions.checkNotNull(adapter);
@@ -717,6 +730,46 @@
}
}
+ @Override
+ public boolean getBindInstantServiceAllowed(@UserIdInt int userId) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != SHELL_UID && callingUid != ROOT_UID) {
+ throw new SecurityException("Can only be called by uid " + SHELL_UID
+ + " or " + ROOT_UID);
+ }
+
+ final UserState userState;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(userId, false);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return userState.getBindInstantServiceAllowed();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void setBindInstantServiceAllowed(@UserIdInt int userId, boolean allowed) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != SHELL_UID && callingUid != ROOT_UID) {
+ throw new SecurityException("Can only be called by uid " + SHELL_UID
+ + " or " + ROOT_UID);
+ }
+
+ final UserState userState;
+ synchronized (mLock) {
+ userState = getOrCreateUserStateLocked(userId, false);
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ userState.setBindInstantServiceAllowed(allowed);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private boolean isPrintingEnabled() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING,
Binder.getCallingUserHandle());
@@ -773,7 +826,7 @@
List<ResolveInfo> installedServices = mContext.getPackageManager()
.queryIntentServicesAsUser(intent,
- GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
+ GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING | MATCH_INSTANT,
getChangingUserId());
return installedServices != null && !installedServices.isEmpty();
@@ -988,7 +1041,7 @@
return appId;
}
final int callingAppId = UserHandle.getAppId(callingUid);
- if (appId == callingAppId || callingAppId == Process.SHELL_UID
+ if (appId == callingAppId || callingAppId == SHELL_UID
|| callingAppId == Process.SYSTEM_UID) {
return appId;
}
diff --git a/services/print/java/com/android/server/print/PrintShellCommand.java b/services/print/java/com/android/server/print/PrintShellCommand.java
new file mode 100644
index 0000000..11642e5
--- /dev/null
+++ b/services/print/java/com/android/server/print/PrintShellCommand.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.print;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.print.IPrintManager;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command implementation for the print manager service
+ */
+final class PrintShellCommand extends ShellCommand {
+ final @NonNull IPrintManager mService;
+
+ PrintShellCommand(@NonNull IPrintManager service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(@Nullable String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "get-bind-instant-service-allowed": {
+ return runGetBindInstantServiceAllowed();
+ }
+ case "set-bind-instant-service-allowed": {
+ return runSetBindInstantServiceAllowed();
+ }
+ }
+ return -1;
+ }
+
+ private int runGetBindInstantServiceAllowed() {
+ final Integer userId = parseUserId();
+ if (userId == null) {
+ return -1;
+ }
+ try {
+ getOutPrintWriter().println(
+ Boolean.toString(mService.getBindInstantServiceAllowed(userId)));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return 0;
+ }
+
+ private int runSetBindInstantServiceAllowed() {
+ final Integer userId = parseUserId();
+ if (userId == null) {
+ return -1;
+ }
+ final String allowed = getNextArgRequired();
+ if (allowed == null) {
+ getErrPrintWriter().println("Error: no true/false specified");
+ return -1;
+ }
+ try {
+ mService.setBindInstantServiceAllowed(userId, Boolean.parseBoolean(allowed));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return 0;
+ }
+
+ private @Nullable Integer parseUserId() {
+ final String option = getNextOption();
+ if (option != null) {
+ if (option.equals("--user")) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Unknown option: " + option);
+ return null;
+ }
+ }
+ return UserHandle.USER_SYSTEM;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Print service commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println(" set-bind-instant-service-allowed [--user <USER_ID>] true|false ");
+ pw.println(" Set whether binding to print services provided by instant apps is "
+ + "allowed.");
+ pw.println(" get-bind-instant-service-allowed [--user <USER_ID>]");
+ pw.println(" Get whether binding to print services provided by instant apps is "
+ + "allowed.");
+ }
+}
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index d4cbe7b..4f0d6f1 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -571,8 +571,8 @@
mBinding = true;
boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- new UserHandle(mUserId));
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_ALLOW_INSTANT, new UserHandle(mUserId));
if (!wasBound) {
if (DEBUG) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 62185d7..4fbc14c 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.PackageManager.MATCH_INSTANT;
import static com.android.internal.print.DumpUtils.writePrintJobInfo;
import static com.android.internal.print.DumpUtils.writePrinterId;
@@ -155,6 +156,11 @@
*/
private RemotePrintServiceRecommendationService mPrintServiceRecommendationsService;
+ /**
+ * Can services from instant apps be bound? (usually disabled, only used by testing)
+ */
+ private boolean mIsInstantServiceAllowed;
+
public UserState(Context context, int userId, Object lock, boolean lowPriority) {
mContext = context;
mUserId = userId;
@@ -872,9 +878,14 @@
private void readInstalledPrintServicesLocked() {
Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
+ int queryIntentFlags = GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING;
+
+ if (mIsInstantServiceAllowed) {
+ queryIntentFlags |= MATCH_INSTANT;
+ }
+
List<ResolveInfo> installedServices = mContext.getPackageManager()
- .queryIntentServicesAsUser(mQueryIntent,
- GET_SERVICES | GET_META_DATA | MATCH_DEBUG_TRIAGED_MISSING, mUserId);
+ .queryIntentServicesAsUser(mQueryIntent, queryIntentFlags, mUserId);
final int installedCount = installedServices.size();
for (int i = 0, count = installedCount; i < count; i++) {
@@ -1185,6 +1196,18 @@
}
}
+ public boolean getBindInstantServiceAllowed() {
+ return mIsInstantServiceAllowed;
+ }
+
+ public void setBindInstantServiceAllowed(boolean allowed) {
+ synchronized (mLock) {
+ mIsInstantServiceAllowed = allowed;
+
+ updateIfNeededLocked();
+ }
+ }
+
private abstract class PrintJobStateChangeListenerRecord implements DeathRecipient {
@NonNull final IPrintJobStateChangeListener listener;
final int appId;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 18e842f..1520859 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -243,7 +243,7 @@
if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
- anyBoolean(), any(), any(), any());
+ anyBoolean(), anyBoolean(), any(), any(), any());
}
try {
@@ -302,7 +302,7 @@
// always allow test to start activity.
doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
- anyBoolean(), any(), any(), any());
+ anyBoolean(), anyBoolean(), any(), any(), any());
// instrument the stack and task used.
final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 376cc64..3dde7f1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -104,6 +104,7 @@
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.text.Html;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -2731,6 +2732,56 @@
}
@Test
+ public void testVisualDifference_inboxStyle() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setStyle(new Notification.InboxStyle()
+ .addLine("line1").addLine("line2"));
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setStyle(new Notification.InboxStyle()
+ .addLine("line1").addLine("line2_changed"));
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r2)); // line 2 changed unnoticed
+
+ Notification.Builder nb3 = new Notification.Builder(mContext, "")
+ .setStyle(new Notification.InboxStyle()
+ .addLine("line1"));
+ StatusBarNotification sbn3 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb3.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r3 =
+ new NotificationRecord(mContext, sbn3, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r3)); // line 2 removed unnoticed
+
+ Notification.Builder nb4 = new Notification.Builder(mContext, "")
+ .setStyle(new Notification.InboxStyle()
+ .addLine("line1").addLine("line2").addLine("line3"));
+ StatusBarNotification sbn4 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb4.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r4 =
+ new NotificationRecord(mContext, sbn4, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r4)); // line 3 added unnoticed
+
+ Notification.Builder nb5 = new Notification.Builder(mContext, "")
+ .setContentText("not an inbox");
+ StatusBarNotification sbn5 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb5.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r5 =
+ new NotificationRecord(mContext, sbn5, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r5)); // changed Styles, went unnoticed
+ }
+
+ @Test
public void testVisualDifference_diffText() {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setContentText("foo");
@@ -2750,6 +2801,63 @@
}
@Test
+ public void testVisualDifference_sameText() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentText("foo");
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setContentText("foo");
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
+ public void testVisualDifference_sameTextButStyled() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentText(Html.fromHtml("<b>foo</b>"));
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setContentText(Html.fromHtml("<b>foo</b>"));
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
+ public void testVisualDifference_diffTextButStyled() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setContentText(Html.fromHtml("<b>foo</b>"));
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setContentText(Html.fromHtml("<b>bar</b>"));
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertTrue(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
public void testVisualDifference_diffProgress() {
Notification.Builder nb1 = new Notification.Builder(mContext, "")
.setProgress(100, 90, false);
@@ -2788,6 +2896,25 @@
}
@Test
+ public void testVisualDifference_sameProgressStillDone() {
+ Notification.Builder nb1 = new Notification.Builder(mContext, "")
+ .setProgress(100, 100, false);
+ StatusBarNotification sbn1 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb1.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r1 =
+ new NotificationRecord(mContext, sbn1, mock(NotificationChannel.class));
+
+ Notification.Builder nb2 = new Notification.Builder(mContext, "")
+ .setProgress(100, 100, false);
+ StatusBarNotification sbn2 = new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0,
+ nb2.build(), new UserHandle(mUid), null, 0);
+ NotificationRecord r2 =
+ new NotificationRecord(mContext, sbn2, mock(NotificationChannel.class));
+
+ assertFalse(mService.isVisuallyInterruptive(r1, r2));
+ }
+
+ @Test
public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() {
// post 2 notification from this package
final NotificationRecord notif1 = generateNotificationRecord(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 36ec221..975fbcc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -221,10 +221,15 @@
@Test
public void testBigPictureChange() {
+ Bitmap bitA = mock(Bitmap.class);
+ when(bitA.getGenerationId()).thenReturn(100);
+ Bitmap bitB = mock(Bitmap.class);
+ when(bitB.getGenerationId()).thenReturn(200);
+
Notification.Builder nBigPic1 = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+ .setStyle(new Notification.BigPictureStyle().bigPicture(bitA));
Notification.Builder nBigPic2 = new Notification.Builder(mContext, "test")
- .setStyle(new Notification.BigPictureStyle().bigPicture(mock(Bitmap.class)));
+ .setStyle(new Notification.BigPictureStyle().bigPicture(bitB));
assertTrue(Notification.areStyledNotificationsVisiblyDifferent(nBigPic1, nBigPic2));
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index d25fd3f..49fbd8f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -477,4 +477,5 @@
int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049;
int RIL_UNSOL_ICC_SLOT_STATUS = 1050;
int RIL_UNSOL_KEEPALIVE_STATUS = 1051;
+ int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1052;
}
diff --git a/tests/testables/src/android/testing/TestableInstrumentation.java b/tests/testables/src/android/testing/TestableInstrumentation.java
index 93fed85..3207b48 100644
--- a/tests/testables/src/android/testing/TestableInstrumentation.java
+++ b/tests/testables/src/android/testing/TestableInstrumentation.java
@@ -77,7 +77,7 @@
private TestLooperManager mManager;
public MainLooperManager() {
- mMainHandler = new Handler(Looper.getMainLooper());
+ mMainHandler = Handler.createAsync(Looper.getMainLooper());
startManaging();
}
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index f1a7092..f8d223a 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -51,12 +51,12 @@
this(acquireLooperManager(l), l);
}
- private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
+ private TestableLooper(TestLooperManager wrapper, Looper l) {
mQueueWrapper = wrapper;
setupQueue(l);
}
- private TestableLooper(Looper looper, boolean b) throws Exception {
+ private TestableLooper(Looper looper, boolean b) {
setupQueue(looper);
}
@@ -64,7 +64,7 @@
return mLooper;
}
- private void setupQueue(Looper l) throws Exception {
+ private void setupQueue(Looper l) {
mLooper = l;
mQueue = mLooper.getQueue();
mHandler = new Handler(mLooper);
@@ -75,7 +75,7 @@
* the looper will not be available for any subsequent tests. This is
* automatically handled for tests using {@link RunWithLooper}.
*/
- public void destroy() throws NoSuchFieldException, IllegalAccessException {
+ public void destroy() {
mQueueWrapper.release();
if (mLooper == Looper.getMainLooper()) {
TestableInstrumentation.releaseMain();
@@ -133,7 +133,7 @@
if (mMessageHandler != null) {
if (mMessageHandler.onMessageHandled(result)) {
- result.getTarget().dispatchMessage(result);
+ mQueueWrapper.execute(result);
mQueueWrapper.recycle(result);
} else {
mQueueWrapper.recycle(result);
@@ -141,7 +141,7 @@
return false;
}
} else {
- result.getTarget().dispatchMessage(result);
+ mQueueWrapper.execute(result);
mQueueWrapper.recycle(result);
}
} else {
@@ -238,7 +238,7 @@
super(base.getMethod());
mLooper = other.mLooper;
mTestableLooper = other;
- mHandler = new Handler(mLooper);
+ mHandler = Handler.createAsync(mLooper);
}
public static FrameworkMethod get(FrameworkMethod base, boolean setAsMain, Object test) {
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 13e72ba..25f6a488 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -158,4 +159,22 @@
public void testMainLooperAnnotation() {
assertEquals(Looper.myLooper(), Looper.getMainLooper());
}
+
+ @Test
+ public void testCorrectLooperExecution() throws Exception {
+ boolean[] hasRun = new boolean[] { false };
+ Runnable r = () -> {
+ assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper());
+ hasRun[0] = true;
+ };
+ TestableLooper testableLooper = new TestableLooper(Looper.getMainLooper());
+ try {
+ new Handler(Looper.getMainLooper()).post(r);
+ testableLooper.processAllMessages();
+
+ assertTrue(hasRun[0]);
+ } finally {
+ testableLooper.destroy();
+ }
+ }
}
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index ffca466..a8411aa 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -486,12 +486,8 @@
return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
UNSUPPORTED_FLAGS = frozenset({
- flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('DG'),
- flag_sequence('EA'), flag_sequence('EH'), flag_sequence('FK'),
- flag_sequence('GF'), flag_sequence('GP'), flag_sequence('GS'),
- flag_sequence('MF'), flag_sequence('MQ'), flag_sequence('NC'),
- flag_sequence('PM'), flag_sequence('RE'), flag_sequence('TF'),
- flag_sequence('WF'), flag_sequence('XK'), flag_sequence('YT'),
+ flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('MQ'),
+ flag_sequence('RE'), flag_sequence('TF'),
})
EQUIVALENT_FLAGS = {
@@ -531,10 +527,17 @@
ZWJ_IDENTICALS = {
# KISS
(0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
- # FAMILY
- (0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A,
}
+SAME_FLAG_MAPPINGS = [
+ # Diego Garcia and British Indian Ocean Territory
+ ((0x1F1EE, 0x1F1F4), (0x1F1E9, 0x1F1EC)),
+ # St. Martin and France
+ ((0x1F1F2, 0x1F1EB), (0x1F1EB, 0x1F1F7)),
+ # Spain and Ceuta & Melilla
+ ((0x1F1EA, 0x1F1F8), (0x1F1EA, 0x1F1E6)),
+]
+
ZWJ = 0x200D
FEMALE_SIGN = 0x2640
MALE_SIGN = 0x2642
@@ -636,6 +639,9 @@
all_sequences.add(reversed_seq)
equivalent_emoji[reversed_seq] = sequence
+ for first, second in SAME_FLAG_MAPPINGS:
+ equivalent_emoji[first] = second
+
# Remove unsupported flags
all_sequences.difference_update(UNSUPPORTED_FLAGS)
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 4e202df..3689a8f 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -421,7 +421,7 @@
printf(" NULL),\n");
break;
case SECTION_DUMPSYS:
- printf(" new DumpsysSection(%d, \"%s\",", field->number(),
+ printf(" new DumpsysSection(%d, %s,", field->number(),
s.userdebug_and_eng_only() ? "true" : "false");
splitAndPrint(s.args());
printf(" NULL),\n");