Merge "Do the sorting for the ShareSheet asynchronously."
diff --git a/api/system-current.txt b/api/system-current.txt
index 4bba111..5f173cf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26239,10 +26239,16 @@
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
+ method public int calculateBadge(int);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
+ field public static final int BADGING_4K = 30; // 0x1e
+ field public static final int BADGING_HD = 20; // 0x14
+ field public static final int BADGING_NONE = 0; // 0x0
+ field public static final int BADGING_SD = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
field public final android.os.Bundle attributes;
field public final boolean meteredHint;
@@ -26250,6 +26256,9 @@
field public final android.net.RssiCurve rssiCurve;
}
+ public static abstract class ScoredNetwork.Badging implements java.lang.annotation.Annotation {
+ }
+
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 94e5187..7e3dd77 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
@@ -24,6 +25,8 @@
import java.lang.Math;
import java.lang.UnsupportedOperationException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -33,6 +36,15 @@
*/
@SystemApi
public class ScoredNetwork implements Parcelable {
+
+ /**
+ * Key used with the {@link #attributes} bundle to define the badging curve.
+ *
+ * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link
+ * Badging} enums.
+ */
+ public static final String ATTRIBUTES_KEY_BADGING_CURVE =
+ "android.net.attributes.key.BADGING_CURVE";
/**
* Extra used with {@link #attributes} to specify whether the
* network is believed to have a captive portal.
@@ -58,6 +70,15 @@
public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
"android.net.attributes.key.RANKING_SCORE_OFFSET";
+ @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Badging {}
+
+ public static final int BADGING_NONE = 0;
+ public static final int BADGING_SD = 10;
+ public static final int BADGING_HD = 20;
+ public static final int BADGING_4K = 30;
+
/** A {@link NetworkKey} uniquely identifying this network. */
public final NetworkKey networkKey;
@@ -249,6 +270,25 @@
}
}
+ /**
+ * Return the {@link Badging} enum for this network for the given RSSI, derived from the
+ * badging curve.
+ *
+ * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned.
+ *
+ * @param rssi The rssi level for which the badge should be calculated
+ */
+ @Badging
+ public int calculateBadge(int rssi) {
+ if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) {
+ RssiCurve badgingCurve =
+ attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE);
+ return badgingCurve.lookupScore(rssi);
+ }
+
+ return BADGING_NONE;
+ }
+
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
new Parcelable.Creator<ScoredNetwork>() {
@Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68d8c55..cb19ff9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2075,6 +2075,7 @@
@SystemApi @hide -->
<permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
<!-- ========================================= -->
<!-- Permissions for special development tools -->
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 9c3346e..e818c56 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -166,4 +166,52 @@
assertTrue(newNetwork.meteredHint);
assertNull(newNetwork.attributes);
}
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadgeWhenNoAttributesBundle() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadgeWhenNoBadgingCurveInBundle() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturn4kBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_4K);
+ assertEquals(ScoredNetwork.BADGING_4K, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnHdBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_HD);
+ assertEquals(ScoredNetwork.BADGING_HD, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnSdBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_SD);
+ assertEquals(ScoredNetwork.BADGING_SD, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_NONE);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ private ScoredNetwork buildScoredNetworkWithGivenBadgeForTestRssi(int badge) {
+ RssiCurve badgingCurve =
+ new RssiCurve(RSSI_START, 10, new byte[] {0, 0, 0, 0, 0, 0, (byte) badge});
+ Bundle attr = new Bundle();
+ attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgingCurve);
+ return new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ }
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 77a0fbd..588376f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2291,7 +2291,7 @@
* {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
*/
private synchronized void setSubtitleAnchor() {
- if (mSubtitleController == null) {
+ if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5c2787b..5574753 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -427,6 +427,7 @@
android:supportsPictureInPicture="true"
android:stateNotNeeded="true"
android:taskAffinity=""
+ android:launchMode="singleTop"
androidprv:alwaysFocusable="true" />
<!-- platform logo easter egg activity -->
diff --git a/packages/SystemUI/res/color/tint_color_selector.xml b/packages/SystemUI/res/color/tint_color_selector.xml
new file mode 100644
index 0000000..cb24425
--- /dev/null
+++ b/packages/SystemUI/res/color/tint_color_selector.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="0.25"
+ android:color="?android:attr/colorForeground"/>
+ <item android:state_activated="false"
+ android:alpha="0.3"
+ android:color="?android:attr/colorForeground"/>
+ <item android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
index 736cfd8..83f46e5 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
@@ -20,7 +20,8 @@
android:width="48dp"
android:viewportHeight="48"
android:viewportWidth="48"
- android:tint="@color/qs_tile_tint_unavailable" >
+ android:alpha="0.25"
+ android:tint="?android:attr/colorControlNormal" >
<group
android:name="ic_hotspot"
android:translateX="23.97354"
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 054bfab..cadc09b 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -19,7 +19,7 @@
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#33000000">
+ android:background="#66000000">
<LinearLayout
android:id="@+id/actions"
@@ -45,7 +45,7 @@
android:text="@string/pip_phone_dismiss"
android:background="?android:selectableItemBackground" />
<TextView
- android:id="@+id/minimize"
+ android:id="@+id/expand"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -53,7 +53,7 @@
android:textSize="12sp"
android:textColor="#ffffffff"
android:fontFamily="sans-serif"
- android:text="@string/pip_phone_minimize"
+ android:text="@string/pip_phone_expand"
android:background="?android:selectableItemBackground" />
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c7149df..ba8c644 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -144,10 +144,6 @@
<color name="remote_input_accent">#eeeeee</color>
- <color name="qs_tile_tint_unavailable">#40ffffff</color>
- <color name="qs_tile_tint_inactive">#4dffffff</color>
- <color name="qs_tile_tint_active">#ffffffff</color>
-
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fc1d816..614472a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -68,7 +68,7 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
- <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+ <item name="android:windowBackground">@null</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index c4698c3..07ef5e0 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -491,6 +491,12 @@
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ mFramePaint.setColorFilter(colorFilter);
+ mBatteryPaint.setColorFilter(colorFilter);
+ mWarningTextPaint.setColorFilter(colorFilter);
+ mTextPaint.setColorFilter(colorFilter);
+ mBoltPaint.setColorFilter(colorFilter);
+ mPlusPaint.setColorFilter(colorFilter);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fe8ee6f..e4f7816 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -16,13 +16,16 @@
package com.android.systemui.pip.phone;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.PointF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -30,11 +33,15 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import java.util.ArrayList;
@@ -47,28 +54,40 @@
private static final String TAG = "PipMenuActivity";
- public static final int MESSAGE_FINISH_SELF = 1;
- public static final int MESSAGE_UPDATE_ACTIONS = 2;
+ public static final int MESSAGE_SHOW_MENU = 1;
+ public static final int MESSAGE_HIDE_MENU = 2;
+ public static final int MESSAGE_UPDATE_ACTIONS = 3;
private static final long INITIAL_DISMISS_DELAY = 2000;
private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
+ private static final long MENU_FADE_DURATION = 125;
- private List<RemoteAction> mActions = new ArrayList<>();
+ private boolean mMenuVisible;
+ private final List<RemoteAction> mActions = new ArrayList<>();
+ private View mMenuContainer;
private View mDismissButton;
- private View mMinimizeButton;
+ private View mExpandButton;
+ private ObjectAnimator mMenuContainerAnimator;
+
+ private PointF mDownPosition = new PointF();
+ private PointF mDownDelta = new PointF();
+ private ViewConfiguration mViewConfig;
private Handler mHandler = new Handler();
private Messenger mToControllerMessenger;
private Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MESSAGE_SHOW_MENU:
+ showMenu();
+ break;
+ case MESSAGE_HIDE_MENU:
+ hideMenu();
+ break;
case MESSAGE_UPDATE_ACTIONS:
setActions(((ParceledListSlice) msg.obj).getList());
break;
- case MESSAGE_FINISH_SELF:
- finish();
- break;
}
}
});
@@ -76,12 +95,17 @@
private final Runnable mFinishRunnable = new Runnable() {
@Override
public void run() {
- finish();
+ hideMenu();
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
+ // Set the flags to allow us to watch for outside touches and also hide the menu and start
+ // manipulating the PIP in the same touch gesture
+ mViewConfig = ViewConfiguration.get(this);
+ getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY);
+
super.onCreate(savedInstanceState);
setContentView(R.layout.pip_menu_activity);
@@ -94,44 +118,74 @@
setActions(actions.getList());
}
- findViewById(R.id.menu).setOnClickListener((v) -> {
+ mMenuContainer = findViewById(R.id.menu);
+ mMenuContainer.setOnClickListener((v) -> {
expandPip();
});
mDismissButton = findViewById(R.id.dismiss);
mDismissButton.setOnClickListener((v) -> {
dismissPip();
});
- mMinimizeButton = findViewById(R.id.minimize);
- mMinimizeButton.setOnClickListener((v) -> {
- minimizePip();
+ mExpandButton = findViewById(R.id.expand);
+ mExpandButton.setOnClickListener((v) -> {
+ expandPip();
});
+
+ notifyActivityCallback(mMessenger);
+ showMenu();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ showMenu();
}
@Override
protected void onStart() {
super.onStart();
- notifyActivityVisibility(true);
+ notifyMenuVisibility(true);
repostDelayedFinish(INITIAL_DISMISS_DELAY);
}
@Override
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+ if (!isInPictureInPictureMode) {
+ finish();
+ }
+ }
+
+ @Override
public void onUserInteraction() {
repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
}
@Override
- protected void onStop() {
- super.onStop();
- finish();
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ // On the first action outside the window, hide the menu
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_OUTSIDE:
+ hideMenu();
+ break;
+ case MotionEvent.ACTION_DOWN:
+ mDownPosition.set(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
+ if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() && mMenuVisible) {
+ hideMenu();
+ mMenuVisible = false;
+ }
+ }
+ return super.dispatchTouchEvent(ev);
}
@Override
public void finish() {
- View v = getWindow().getDecorView();
- v.removeCallbacks(mFinishRunnable);
- notifyActivityVisibility(false);
+ notifyActivityCallback(null);
super.finish();
- overridePendingTransition(0, R.anim.forced_resizable_exit);
+ // Hide without an animation (the menu should already be invisible at this point)
+ overridePendingTransition(0, 0);
}
@Override
@@ -139,6 +193,51 @@
// Do nothing
}
+ private void showMenu() {
+ if (!mMenuVisible) {
+ if (mMenuContainerAnimator != null) {
+ mMenuContainerAnimator.cancel();
+ }
+
+ notifyMenuVisibility(true);
+ mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+ mMenuContainer.getAlpha(), 1f);
+ mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+ mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ repostDelayedFinish(INITIAL_DISMISS_DELAY);
+ }
+ });
+ mMenuContainerAnimator.start();
+ }
+ }
+
+ private void hideMenu() {
+ hideMenu(null /* animationFinishedRunnable */);
+ }
+
+ private void hideMenu(final Runnable animationFinishedRunnable) {
+ if (mMenuVisible) {
+ cancelDelayedFinish();
+ notifyMenuVisibility(false);
+ mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+ mMenuContainer.getAlpha(), 0f);
+ mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+ mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (animationFinishedRunnable != null) {
+ animationFinishedRunnable.run();
+ }
+ }
+ });
+ mMenuContainerAnimator.start();
+ }
+ }
+
private void setActions(List<RemoteAction> actions) {
mActions.clear();
mActions.addAll(actions);
@@ -173,17 +272,19 @@
}
}
- private void notifyActivityVisibility(boolean visible) {
+ private void notifyMenuVisibility(boolean visible) {
+ mMenuVisible = visible;
Message m = Message.obtain();
- m.what = PipMenuActivityController.MESSAGE_ACTIVITY_VISIBILITY_CHANGED;
+ m.what = PipMenuActivityController.MESSAGE_MENU_VISIBILITY_CHANGED;
m.arg1 = visible ? 1 : 0;
- m.replyTo = visible ? mMessenger : null;
sendMessage(m, "Could not notify controller of PIP menu visibility");
}
private void expandPip() {
- sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
- "Could not notify controller to expand PIP");
+ hideMenu(() -> {
+ sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
+ "Could not notify controller to expand PIP");
+ });
}
private void minimizePip() {
@@ -192,8 +293,17 @@
}
private void dismissPip() {
- sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
- "Could not notify controller to dismiss PIP");
+ hideMenu(() -> {
+ sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
+ "Could not notify controller to dismiss PIP");
+ });
+ }
+
+ private void notifyActivityCallback(Messenger callback) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
+ m.replyTo = callback;
+ sendMessage(m, "Could not notify controller of activity finished");
}
private void sendEmptyMessage(int what, String errorMsg) {
@@ -210,6 +320,11 @@
}
}
+ private void cancelDelayedFinish() {
+ View v = getWindow().getDecorView();
+ v.removeCallbacks(mFinishRunnable);
+ }
+
private void repostDelayedFinish(long delay) {
View v = getWindow().getDecorView();
v.removeCallbacks(mFinishRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 64e2d1a..0350cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -25,10 +25,11 @@
public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
public static final String EXTRA_ACTIONS = "actions";
- public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 100;
+ public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
public static final int MESSAGE_EXPAND_PIP = 101;
public static final int MESSAGE_MINIMIZE_PIP = 102;
public static final int MESSAGE_DISMISS_PIP = 103;
+ public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
/**
* A listener interface to receive notification on changes in PIP.
@@ -67,34 +68,25 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_ACTIVITY_VISIBILITY_CHANGED: {
+ case MESSAGE_MENU_VISIBILITY_CHANGED: {
boolean visible = msg.arg1 > 0;
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipMenuVisibilityChanged(visible);
- }
- mToActivityMessenger = msg.replyTo;
+ mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
break;
}
case MESSAGE_EXPAND_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipExpand();
- }
+ mListeners.forEach(l -> l.onPipExpand());
break;
}
case MESSAGE_MINIMIZE_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipMinimize();
- }
+ mListeners.forEach(l -> l.onPipMinimize());
break;
}
case MESSAGE_DISMISS_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipDismiss();
- }
+ mListeners.forEach(l -> l.onPipDismiss());
+ break;
+ }
+ case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
+ mToActivityMessenger = msg.replyTo;
break;
}
}
@@ -121,24 +113,34 @@
* Shows the menu activity.
*/
public void showMenu() {
- // Start the menu activity on the top task of the pinned stack
- try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
- pinnedStackInfo.taskIds.length > 0) {
- Intent intent = new Intent(mContext, PipMenuActivity.class);
- intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
- intent.putExtra(EXTRA_ACTIONS, mActions);
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchTaskId(
- pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
- options.setTaskOverlay(true);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- } else {
- Log.e(TAG, "No PIP tasks found");
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify menu to show", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error showing PIP menu activity", e);
+ } else {
+ // Start the menu activity on the top task of the pinned stack
+ try {
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+ pinnedStackInfo.taskIds.length > 0) {
+ Intent intent = new Intent(mContext, PipMenuActivity.class);
+ intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+ intent.putExtra(EXTRA_ACTIONS, mActions);
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+ options.setLaunchTaskId(
+ pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+ options.setTaskOverlay(true);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ } else {
+ Log.e(TAG, "No PIP tasks found");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
}
}
@@ -148,13 +150,12 @@
public void hideMenu() {
if (mToActivityMessenger != null) {
Message m = Message.obtain();
- m.what = PipMenuActivity.MESSAGE_FINISH_SELF;
+ m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
try {
mToActivityMessenger.send(m);
} catch (RemoteException e) {
- Log.e(TAG, "Could not notify menu activity to finish", e);
+ Log.e(TAG, "Could not notify menu to hide", e);
}
- mToActivityMessenger = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index ff3cc79..380e4683 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -174,7 +174,7 @@
@Override
public void onPipDismiss() {
- animateDismissPinnedStack(mPinnedStackBounds);
+ BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
}
}
@@ -328,26 +328,31 @@
* Registers the input consumer.
*/
private void registerInputConsumer() {
- final InputChannel inputChannel = new InputChannel();
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to create PIP input consumer", e);
+ if (mInputEventReceiver == null) {
+ final InputChannel inputChannel = new InputChannel();
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to create PIP input consumer", e);
+ }
+ mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
}
- mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
}
/**
* Unregisters the input consumer.
*/
private void unregisterInputConsumer() {
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ if (mInputEventReceiver != null) {
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ }
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
- mInputEventReceiver.dispose();
}
/**
@@ -761,10 +766,6 @@
private PipTouchGesture mTapThroughGesture = new PipTouchGesture() {
@Override
boolean onMove(PipTouchState touchState) {
- if (mEnableTapThrough && touchState.startedDragging()) {
- mIsTappingThrough = false;
- mMenuController.hideMenu();
- }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 1fcb45b..9e3889b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.qs.external;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -24,7 +27,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -37,19 +39,14 @@
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.IWindowManager;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.statusbar.phone.QSTileHost;
import libcore.util.Objects;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-
public class CustomTile extends QSTile<QSTile.State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -301,7 +298,7 @@
tileState = Tile.STATE_UNAVAILABLE;
drawable = mDefaultIcon.loadDrawable(mAppContext);
}
- int color = mContext.getColor(getColor(tileState));
+ final int color = TileColorPicker.getInstance(mContext).getColor(tileState);
drawable.setTint(color);
state.icon = mHasRes ? new DrawableIconWithRes(drawable, icon.getResId())
: new DrawableIcon(drawable);
@@ -335,18 +332,6 @@
});
}
- private static int getColor(int state) {
- switch (state) {
- case Tile.STATE_UNAVAILABLE:
- return R.color.qs_tile_tint_unavailable;
- case Tile.STATE_INACTIVE:
- return R.color.qs_tile_tint_inactive;
- case Tile.STATE_ACTIVE:
- return R.color.qs_tile_tint_active;
- }
- return 0;
- }
-
public static String toSpec(ComponentName name) {
return PREFIX + name.flattenToShortString() + ")";
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
new file mode 100644
index 0000000..0cc17ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ */
+package com.android.systemui.qs.external;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.annotation.VisibleForTesting;
+import com.android.systemui.R;
+
+public class TileColorPicker {
+ @VisibleForTesting static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
+ @VisibleForTesting static final int[] ENABLE_STATE_SET = {android.R.attr.state_enabled,
+ android.R.attr.state_activated};
+ @VisibleForTesting static final int[] INACTIVE_STATE_SET = {-android.R.attr.state_activated};
+ private static TileColorPicker sInstance;
+
+ private ColorStateList mColorStateList;
+
+ private TileColorPicker(Context context) {
+ mColorStateList = context.getResources().
+ getColorStateList(R.color.tint_color_selector, context.getTheme());
+ }
+
+ public static TileColorPicker getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new TileColorPicker(context);
+ }
+ return sInstance;
+ }
+
+ public int getColor(int state) {
+ final int defaultColor = 0;
+
+ switch (state) {
+ case Tile.STATE_UNAVAILABLE:
+ return mColorStateList.getColorForState(DISABLE_STATE_SET, defaultColor);
+ case Tile.STATE_INACTIVE:
+ return mColorStateList.getColorForState(INACTIVE_STATE_SET, defaultColor);
+ case Tile.STATE_ACTIVE:
+ return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+ default:
+ return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index fc1c1ac..87b00a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
@@ -40,6 +43,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.BatteryController;
import java.text.NumberFormat;
@@ -120,6 +124,8 @@
context.getColor(R.color.batterymeter_frame_color));
drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
drawable.onPowerSaveChanged(mPowerSave);
+ final int color = TileColorPicker.getInstance(context).getColor(Tile.STATE_ACTIVE);
+ drawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
return drawable;
}
@@ -205,6 +211,11 @@
mDrawable.onBatteryLevelChanged(100, false, false);
mDrawable.onPowerSaveChanged(true);
mDrawable.disableShowPercent();
+
+ final int color = TileColorPicker.getInstance(mCurrentView.getContext())
+ .getColor(Tile.STATE_ACTIVE);
+ mDrawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
+
((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
checkbox.setChecked(mPowerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5b1638f..d86aebf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.provider.MediaStore;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.widget.Switch;
@@ -28,6 +29,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.FlashlightController;
/** Quick settings tile: Control flashlight **/
@@ -104,7 +106,8 @@
if (!mFlashlightController.isAvailable()) {
Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable)
.mutate();
- final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+ final int disabledColor = TileColorPicker.getInstance(mContext)
+ .getColor(Tile.STATE_UNAVAILABLE);
icon.setTint(disabledColor);
state.icon = new DrawableIcon(icon);
state.label = new SpannableStringBuilder().append(state.label,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index dcee659..99485bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -22,6 +22,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.widget.Switch;
@@ -31,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.HotspotController;
/** Quick settings tile: Hotspot **/
@@ -124,7 +126,8 @@
boolean wasAirplane = state.isAirplaneMode;
state.isAirplaneMode = mAirplaneMode.getValue() != 0;
if (state.isAirplaneMode) {
- final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+ final int disabledColor = TileColorPicker.getInstance(mContext)
+ .getColor(Tile.STATE_UNAVAILABLE);
state.label = new SpannableStringBuilder().append(state.label,
new ForegroundColorSpan(disabledColor),
SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
new file mode 100644
index 0000000..ba451e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package com.android.systemui.qs.external;
+
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.systemui.R;
+
+import static junit.framework.Assert.assertEquals;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TileColorPickerTest extends SysuiTestCase {
+ private static final int DEFAULT_COLOR = 0;
+
+ private TileColorPicker mTileColorPicker;
+ private ColorStateList mTintColorStateList;
+
+ @Before
+ public void setUp() {
+ mTileColorPicker = TileColorPicker.getInstance(mContext);
+ mTintColorStateList = mContext.getResources().
+ getColorStateList(R.color.tint_color_selector, mContext.getTheme());
+ }
+
+ @Test
+ public void testGetColor_StateUnavailable_ReturnUnavailableColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_UNAVAILABLE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.DISABLE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+
+ @Test
+ public void testGetColor_StateInactive_ReturnInactiveColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_INACTIVE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.INACTIVE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+
+ @Test
+ public void testGetColor_StateActive_ReturnActiveColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_ACTIVE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.ENABLE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 10a19d7..22f9f3a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5980,8 +5980,9 @@
throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
+ final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userId, admin);
synchronized (this) {
- enforceCanSetDeviceOwnerLocked(admin, userId);
+ enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccounts);
final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
if (activeAdmin == null
|| getUserData(userId).mRemovingAdmins.contains(admin)) {
@@ -6217,8 +6218,9 @@
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
+ final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userHandle, who);
synchronized (this) {
- enforceCanSetProfileOwnerLocked(who, userHandle);
+ enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccounts);
if (getActiveAdminUncheckedLocked(who, userHandle) == null
|| getUserData(userHandle).mRemovingAdmins.contains(who)) {
@@ -6551,9 +6553,10 @@
* The profile owner can only be set before the user setup phase has completed,
* except for:
* - SYSTEM_UID
- * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
+ * - adb unless hasIncompatibleAccounts is true.
*/
- private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
+ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+ boolean hasIncompatibleAccounts) {
UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
@@ -6573,7 +6576,7 @@
}
if (isAdb()) {
if ((mIsWatch || hasUserSetupCompleted(userHandle))
- && hasIncompatibleAccountsLocked(userHandle, owner)) {
+ && hasIncompatibleAccounts) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
@@ -6590,12 +6593,14 @@
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) {
+ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+ boolean hasIncompatibleAccounts) {
if (!isAdb()) {
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner, userId, isAdb());
+ final int code = checkDeviceOwnerProvisioningPreConditionLocked(
+ owner, userId, isAdb(), hasIncompatibleAccounts);
switch (code) {
case CODE_OK:
return;
@@ -8904,7 +8909,7 @@
* except for adb command if no accounts or additional users are present on the device.
*/
private int checkDeviceOwnerProvisioningPreConditionLocked(@Nullable ComponentName owner,
- int deviceOwnerUserId, boolean isAdb) {
+ int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccounts) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
}
@@ -8924,7 +8929,7 @@
if (mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
+ if (hasIncompatibleAccounts) {
return CODE_ACCOUNTS_NOT_EMPTY;
}
} else {
@@ -8951,8 +8956,9 @@
private int checkDeviceOwnerProvisioningPreCondition(int deviceOwnerUserId) {
synchronized (this) {
+ // hasIncompatibleAccounts doesn't matter since the caller is not adb.
return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
- deviceOwnerUserId, /* isAdb= */ false);
+ deviceOwnerUserId, /* isAdb= */ false, /* hasIncompatibleAccounts=*/ true);
}
}
@@ -9850,8 +9856,15 @@
* - Otherwise, if there's any account that does not have ..._ALLOWED, or does have
* ..._DISALLOWED, return true.
* - Otherwise return false.
+ *
+ * DO NOT CALL IT WITH THE DPMS LOCK HELD.
*/
- private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
+ private boolean hasIncompatibleAccountsNoLock(int userId, @Nullable ComponentName owner) {
+ if (Thread.holdsLock(this)) {
+ Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held.");
+ return true;
+ }
+
final long token = mInjector.binderClearCallingIdentity();
try {
final AccountManager am = AccountManager.get(mContext);
@@ -9859,22 +9872,30 @@
if (accounts.length == 0) {
return false;
}
+ synchronized (this) {
+ if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
+ Log.w(LOG_TAG,
+ "Non test-only owner can't be installed with existing accounts.");
+ return true;
+ }
+ }
+
final String[] feature_allow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
final String[] feature_disallow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
- // Even if we find incompatible accounts along the way, we still check all accounts
- // for logging.
boolean compatible = true;
for (Account account : accounts) {
if (hasAccountFeatures(am, account, feature_disallow)) {
Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
compatible = false;
+ break;
}
if (!hasAccountFeatures(am, account, feature_allow)) {
Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
compatible = false;
+ break;
}
}
if (compatible) {
@@ -9882,28 +9903,6 @@
} else {
Log.e(LOG_TAG, "Found incompatible accounts");
}
-
- // Then check if the owner is test-only.
- String log;
- if (owner == null) {
- // Owner is unknown. Suppose it's not test-only
- compatible = false;
- log = "Only test-only device/profile owner can be installed with accounts";
- } else if (isAdminTestOnlyLocked(owner, userId)) {
- if (compatible) {
- log = "Installing test-only owner " + owner;
- } else {
- log = "Can't install test-only owner " + owner + " with incompatible accounts";
- }
- } else {
- compatible = false;
- log = "Can't install non test-only owner " + owner + " with accounts";
- }
- if (compatible) {
- Log.w(LOG_TAG, log);
- } else {
- Log.e(LOG_TAG, log);
- }
return !compatible;
} finally {
mInjector.binderRestoreCallingIdentity(token);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 1247b2d..65255d9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
@@ -273,6 +275,7 @@
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
public final TelephonyManager telephonyManager;
+ public final AccountManager accountManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -315,6 +318,7 @@
wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
telephonyManager = mock(TelephonyManager.class);
+ accountManager = mock(AccountManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(context.getPackageManager());
@@ -375,6 +379,7 @@
}
}
);
+ when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
// Create a data directory.
final File dir = new File(dataDir, "user" + userId);
@@ -464,6 +469,8 @@
return powerManager;
case Context.WIFI_SERVICE:
return wifiManager;
+ case Context.ACCOUNT_SERVICE:
+ return accountManager;
}
throw new UnsupportedOperationException();
}