Merge "Enhance recent sync log in dumpsys content" into oc-dr1-dev
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 8eefd25..4966b43 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -382,6 +382,7 @@
oldAnims = mWm.getAnimationScales();
mWm.setAnimationScale(0, 0.0f);
mWm.setAnimationScale(1, 0.0f);
+ mWm.setAnimationScale(2, 0.0f);
}
// Figure out which component we are tring to do.
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 607e6e0..71be632 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -251,5 +251,7 @@
if (mapbase != MAP_FAILED) {
munmap((void *)mapbase, mapsize);
}
- return 0;
+
+ // b/36066697: Avoid running static destructors.
+ _exit(1);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 24c144c..bc6e9cd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,17 +16,9 @@
package android.app;
-import android.graphics.Rect;
-import android.view.ViewRootImpl.ActivityConfigCallback;
-import android.view.autofill.AutofillManager;
-import android.view.autofill.AutofillPopupWindow;
-import android.view.autofill.IAutofillWindowPresenter;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.ToolbarActionBar;
-import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
+import static android.os.Build.VERSION_CODES.O;
+
+import static java.lang.Character.MIN_VALUE;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -62,6 +54,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
@@ -114,15 +107,26 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewManager;
import android.view.ViewRootImpl;
+import android.view.ViewRootImpl.ActivityConfigCallback;
import android.view.Window;
import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillPopupWindow;
+import android.view.autofill.IAutofillWindowPresenter;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ToolbarActionBar;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.policy.DecorView;
+import com.android.internal.policy.PhoneWindow;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -131,9 +135,6 @@
import java.util.HashMap;
import java.util.List;
-import static android.os.Build.VERSION_CODES.O;
-import static java.lang.Character.MIN_VALUE;
-
/**
* An activity is a single, focused thing that the user can do. Almost all
* activities interact with the user, so the Activity class takes care of
@@ -999,7 +1000,8 @@
}
if (savedInstanceState != null) {
mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
- mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID, View.NO_ID);
+ mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID,
+ View.LAST_APP_AUTOFILL_ID);
if (mAutoFillResetNeeded) {
getAutofillManager().onCreate(savedInstanceState);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8c64129..a040520f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -256,28 +256,34 @@
@Override
public void setTheme(int resId) {
- if (mThemeResource != resId) {
- mThemeResource = resId;
- initializeTheme();
+ synchronized (mSync) {
+ if (mThemeResource != resId) {
+ mThemeResource = resId;
+ initializeTheme();
+ }
}
}
@Override
public int getThemeResId() {
- return mThemeResource;
+ synchronized (mSync) {
+ return mThemeResource;
+ }
}
@Override
public Resources.Theme getTheme() {
- if (mTheme != null) {
+ synchronized (mSync) {
+ if (mTheme != null) {
+ return mTheme;
+ }
+
+ mThemeResource = Resources.selectDefaultTheme(mThemeResource,
+ getOuterContext().getApplicationInfo().targetSdkVersion);
+ initializeTheme();
+
return mTheme;
}
-
- mThemeResource = Resources.selectDefaultTheme(mThemeResource,
- getOuterContext().getApplicationInfo().targetSdkVersion);
- initializeTheme();
-
- return mTheme;
}
private void initializeTheme() {
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 266fa7e..4e8277c 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1051,6 +1051,9 @@
public void updateAutofillValue(AutofillValue value) {
mAutofillValue = value;
if (value.isText()) {
+ if (mText == null) {
+ mText = new ViewNodeText();
+ }
mText.mText = value.getTextValue();
}
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d56d5..dfd5996 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -208,6 +208,8 @@
if (wrapper == null) return;
stopAdvertisingSet(wrapper);
+
+ mLegacyAdvertisers.remove(callback);
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index dabe608..4400ad33 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -288,6 +288,7 @@
@Override
public void onActivityDestroyed(Activity activity) {
+ if (activity != getActivity()) return;
try {
mService.stopScan(mRequest, this, getCallingPackage());
} catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fcb99b1..f478071 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -602,6 +602,15 @@
public final static int REQUEST_ID_UNSET = 0;
/**
+ * Static unique request used as a tombstone for NetworkCallbacks that have been unregistered.
+ * This allows to distinguish when unregistering NetworkCallbacks those that were never
+ * registered and those that were already unregistered.
+ * @hide
+ */
+ private final static NetworkRequest ALREADY_UNREGISTERED =
+ new NetworkRequest.Builder().clearCapabilities().build();
+
+ /**
* A NetID indicating no Network is selected.
* Keep in sync with bionic/libc/dns/include/resolv_netid.h
* @hide
@@ -2686,10 +2695,6 @@
public void onNetworkResumed(Network network) {}
private NetworkRequest networkRequest;
-
- private boolean isRegistered() {
- return (networkRequest != null) && (networkRequest.requestId != REQUEST_ID_UNSET);
- }
}
/**
@@ -2856,7 +2861,8 @@
final NetworkRequest request;
try {
synchronized(sCallbacks) {
- if (callback.isRegistered()) {
+ if (callback.networkRequest != null
+ && callback.networkRequest != ALREADY_UNREGISTERED) {
// TODO: throw exception instead and enforce 1:1 mapping of callbacks
// and requests (http://b/20701525).
Log.e(TAG, "NetworkCallback was already registered");
@@ -3302,8 +3308,10 @@
// Find all requests associated to this callback and stop callback triggers immediately.
// Callback is reusable immediately. http://b/20701525, http://b/35921499.
synchronized (sCallbacks) {
- Preconditions.checkArgument(
- networkCallback.isRegistered(), "NetworkCallback was not registered");
+ Preconditions.checkArgument(networkCallback.networkRequest != null,
+ "NetworkCallback was not registered");
+ Preconditions.checkArgument(networkCallback.networkRequest != ALREADY_UNREGISTERED,
+ "NetworkCallback was already unregistered");
for (Map.Entry<NetworkRequest, NetworkCallback> e : sCallbacks.entrySet()) {
if (e.getValue() == networkCallback) {
reqs.add(e.getKey());
@@ -3319,7 +3327,7 @@
// Only remove mapping if rpc was successful.
sCallbacks.remove(r);
}
- networkCallback.networkRequest = null;
+ networkCallback.networkRequest = ALREADY_UNREGISTERED;
}
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 9df315b..a90ee93 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -25,7 +25,6 @@
import android.app.Service;
import android.app.assist.AssistStructure;
import android.content.Intent;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.ICancellationSignal;
@@ -35,9 +34,6 @@
import com.android.internal.os.SomeArgs;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Top-level service of the current autofill service for a given user.
*
@@ -192,6 +188,11 @@
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call
+ * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls
+ * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no
+ * guarantee such method will return the most recent value of the field.
+ *
* @param request the {@link SaveRequest request} to handle.
* See {@link FillResponse} for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index caadc36..cb98c88 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -72,6 +72,15 @@
* Creates a display event receiver.
*
* @param looper The looper to use when invoking callbacks.
+ */
+ public DisplayEventReceiver(Looper looper) {
+ this(looper, VSYNC_SOURCE_APP);
+ }
+
+ /**
+ * Creates a display event receiver.
+ *
+ * @param looper The looper to use when invoking callbacks.
* @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
*/
public DisplayEventReceiver(Looper looper, int vsyncSource) {
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 31cece4..0a73949 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -62,6 +62,12 @@
public static final int VIRTUAL_KEY_RELEASE = 7;
/**
+ * The user has performed a selection/insertion handle move on text field.
+ * @hide
+ */
+ public static final int TEXT_HANDLE_MOVE = 8;
+
+ /**
* This is a private constant. Feel free to renumber as desired.
* @hide
*/
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5f55bdc..04fa637 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -224,6 +224,14 @@
* Constant for {@link #getActionMasked}: A movement has happened outside of the
* normal bounds of the UI element. This does not provide a full gesture,
* but only the initial location of the movement/touch.
+ * <p>
+ * Note: Because the location of any event will be outside the
+ * bounds of the view hierarchy, it will not get dispatched to
+ * any children of a ViewGroup by default. Therefore,
+ * movements with ACTION_OUTSIDE should be handled in either the
+ * root {@link View} or in the appropriate {@link Window.Callback}
+ * (e.g. {@link android.app.Activity} or {@link android.app.Dialog}).
+ * </p>
*/
public static final int ACTION_OUTSIDE = 4;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5b96454..c329db4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -26247,9 +26247,6 @@
mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
}
mTooltipInfo.mTooltipText = tooltipText;
- if (mTooltipInfo.mTooltipPopup != null && mTooltipInfo.mTooltipPopup.isShowing()) {
- mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltipText);
- }
}
}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 55aed52..4c9cf40 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -229,6 +229,11 @@
public abstract boolean isKeyguardGoingAway();
/**
+ * @return Whether the keyguard is showing and not occluded.
+ */
+ public abstract boolean isKeyguardShowingAndNotOccluded();
+
+ /**
* Gets the frame of a window given its token.
*
* @param token The token.
diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java
index 52357ac..3930214 100644
--- a/core/java/com/android/internal/view/TooltipPopup.java
+++ b/core/java/com/android/internal/view/TooltipPopup.java
@@ -91,10 +91,6 @@
return mContentView.getParent() != null;
}
- public void updateContent(CharSequence tooltipText) {
- mMessageView.setText(tooltipText);
- }
-
private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch,
WindowManager.LayoutParams outParams) {
outParams.token = anchorView.getWindowToken();
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 19f779f..cdd3c09 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -58,6 +58,29 @@
jmethodID onTransactID;
} gFields;
+struct JHwBinderHolder : public RefBase {
+ JHwBinderHolder() {}
+
+ sp<JHwBinder> get(JNIEnv *env, jobject obj) {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<JHwBinder> binder = mBinder.promote();
+
+ if (binder == NULL) {
+ binder = new JHwBinder(env, obj);
+ mBinder = binder;
+ }
+
+ return binder;
+ }
+
+private:
+ Mutex mLock;
+ wp<JHwBinder> mBinder;
+
+ DISALLOW_COPY_AND_ASSIGN(JHwBinderHolder);
+};
+
// static
void JHwBinder::InitClass(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(
@@ -75,10 +98,10 @@
}
// static
-sp<JHwBinder> JHwBinder::SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) {
- sp<JHwBinder> old =
- (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+sp<JHwBinderHolder> JHwBinder::SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context) {
+ sp<JHwBinderHolder> old =
+ (JHwBinderHolder *)env->GetLongField(thiz, gFields.contextID);
if (context != NULL) {
context->incStrong(NULL /* id */);
@@ -94,27 +117,27 @@
}
// static
-sp<JHwBinder> JHwBinder::GetNativeContext(
+sp<JHwBinder> JHwBinder::GetNativeBinder(
JNIEnv *env, jobject thiz) {
- return (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+ JHwBinderHolder *holder =
+ reinterpret_cast<JHwBinderHolder *>(
+ env->GetLongField(thiz, gFields.contextID));
+
+ return holder->get(env, thiz);
}
JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) {
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
- mObject = env->NewWeakGlobalRef(thiz);
+ mObject = env->NewGlobalRef(thiz);
}
JHwBinder::~JHwBinder() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mObject);
+ env->DeleteGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
status_t JHwBinder::onTransact(
@@ -203,10 +226,10 @@
using namespace android;
static void releaseNativeContext(void *nativeContext) {
- sp<JHwBinder> binder = (JHwBinder *)nativeContext;
+ sp<JHwBinderHolder> context = static_cast<JHwBinderHolder *>(nativeContext);
- if (binder != NULL) {
- binder->decStrong(NULL /* id */);
+ if (context != NULL) {
+ context->decStrong(NULL /* id */);
}
}
@@ -217,8 +240,7 @@
}
static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) {
- sp<JHwBinder> context = new JHwBinder(env, thiz);
-
+ sp<JHwBinderHolder> context = new JHwBinderHolder;
JHwBinder::SetNativeContext(env, thiz, context);
}
@@ -246,7 +268,7 @@
return; // XXX exception already pending?
}
- sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+ sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
/* TODO(b/33440494) this is not right */
sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpHwBase(binder);
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
index fa8fe01..5352f1e 100644
--- a/core/jni/android_os_HwBinder.h
+++ b/core/jni/android_os_HwBinder.h
@@ -24,13 +24,15 @@
namespace android {
+struct JHwBinderHolder;
+
struct JHwBinder : public hardware::BHwBinder {
static void InitClass(JNIEnv *env);
- static sp<JHwBinder> SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context);
+ static sp<JHwBinderHolder> SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context);
- static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+ static sp<JHwBinder> GetNativeBinder(JNIEnv *env, jobject thiz);
JHwBinder(JNIEnv *env, jobject thiz);
@@ -45,7 +47,6 @@
TransactCallback callback);
private:
- jclass mClass;
jobject mObject;
DISALLOW_COPY_AND_ASSIGN(JHwBinder);
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index b21ea828..6ea809a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -169,7 +169,6 @@
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -182,9 +181,6 @@
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
hardware::Parcel *JHwParcel::getParcel() {
@@ -542,7 +538,7 @@
env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder"));
if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) {
- binder = JHwBinder::GetNativeContext(env, binderObj);
+ binder = JHwBinder::GetNativeBinder(env, binderObj);
} else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) {
binder = JHwRemoteBinder::GetNativeContext(
env, binderObj)->getBinder();
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index f81de9b..f6e6100 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -53,7 +53,6 @@
virtual ~JHwParcel();
private:
- jclass mClass;
jobject mObject;
hardware::Parcel *mParcel;
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f2f8e52..9c2ee9c 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -272,7 +272,6 @@
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -281,9 +280,6 @@
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 77a0278..63aad0a 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -68,7 +68,6 @@
virtual ~JHwRemoteBinder();
private:
- jclass mClass;
jobject mObject;
sp<hardware::IBinder> mBinder;
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 4ef0f4b..b073070b 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -133,8 +133,7 @@
return CopyResult::DestinationInvalid;
}
- // TODO: Add support for RGBA_F16 destinations
- if (bitmap->colorType() == kRGBA_F16_SkColorType) {
+ if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
@@ -148,24 +147,34 @@
GLuint texture;
GLenum format;
+ GLenum internalFormat;
GLenum type;
switch (bitmap->colorType()) {
case kAlpha_8_SkColorType:
format = GL_ALPHA;
+ internalFormat = GL_ALPHA;
type = GL_UNSIGNED_BYTE;
break;
case kRGB_565_SkColorType:
format = GL_RGB;
+ internalFormat = GL_RGB;
type = GL_UNSIGNED_SHORT_5_6_5;
break;
case kARGB_4444_SkColorType:
format = GL_RGBA;
+ internalFormat = GL_RGBA;
type = GL_UNSIGNED_SHORT_4_4_4_4;
break;
+ case kRGBA_F16_SkColorType:
+ format = GL_RGBA;
+ internalFormat = GL_RGBA16F;
+ type = GL_HALF_FLOAT;
+ break;
case kN32_SkColorType:
default:
format = GL_RGBA;
+ internalFormat = GL_RGBA;
type = GL_UNSIGNED_BYTE;
break;
}
@@ -184,7 +193,7 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight,
+ glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight,
0, format, type, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
@@ -220,6 +229,7 @@
ortho.loadOrtho(destWidth, destHeight);
renderState.render(glop, ortho);
+ // TODO: We should convert to linear space when the target is RGBA16F
glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
type, bitmap->getPixels());
}
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 22f53ea..e38cca7 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -57,6 +57,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
public class CaptivePortalLoginActivity extends Activity {
private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
@@ -82,6 +83,8 @@
private ConnectivityManager mCm;
private boolean mLaunchBrowser = false;
private MyWebViewClient mWebViewClient;
+ // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
+ private final AtomicBoolean isDone = new AtomicBoolean(false);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -178,13 +181,13 @@
}
private void done(Result result) {
+ if (isDone.getAndSet(true)) {
+ // isDone was already true: done() already called
+ return;
+ }
if (DBG) {
Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
}
- if (mNetworkCallback != null) {
- mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
- }
logMetricsEvent(result.metricsEvent);
switch (result) {
case DISMISSED:
@@ -244,8 +247,8 @@
public void onDestroy() {
super.onDestroy();
if (mNetworkCallback != null) {
+ // mNetworkCallback is not null if mUrl is not null.
mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
}
if (mLaunchBrowser) {
// Give time for this network to become default. After 500ms just proceed.
diff --git a/packages/SystemUI/res/drawable/ic_mode_edit.xml b/packages/SettingsLib/res/drawable/ic_mode_edit.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_mode_edit.xml
rename to packages/SettingsLib/res/drawable/ic_mode_edit.xml
diff --git a/packages/SystemUI/res-keyguard/values/attrs.xml b/packages/SystemUI/res-keyguard/values/attrs.xml
index d3d60a1..802bd30 100644
--- a/packages/SystemUI/res-keyguard/values/attrs.xml
+++ b/packages/SystemUI/res-keyguard/values/attrs.xml
@@ -41,7 +41,4 @@
<declare-styleable name="CarrierText">
<attr name="allCaps" format="boolean" />
</declare-styleable>
-
- <attr name="pinDividerColor" format="color" />
- <attr name="pinDeleteColor" format="color" />
</resources>
diff --git a/packages/SystemUI/res/layout/navigation_bar_window.xml b/packages/SystemUI/res/layout/navigation_bar_window.xml
index 051bf3a..6fa46d4 100644
--- a/packages/SystemUI/res/layout/navigation_bar_window.xml
+++ b/packages/SystemUI/res/layout/navigation_bar_window.xml
@@ -16,11 +16,11 @@
** limitations under the License.
*/
-->
-<FrameLayout
+<com.android.systemui.statusbar.phone.NavigationBarFrame
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_bar_frame"
android:layout_height="match_parent"
android:layout_width="match_parent">
-</FrameLayout>
+</com.android.systemui.statusbar.phone.NavigationBarFrame>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f942be6..5274b64 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -295,8 +295,6 @@
<item name="darkIconTheme">@style/DualToneDarkTheme</item>
<item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
<item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item>
- <item name="pinDividerColor">@color/pin_divider_color</item>
- <item name="pinDeleteColor">@color/pin_delete_color</item>
<item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 38811ac..ce3068d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -16,13 +16,19 @@
package com.android.keyguard;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionInfo;
import android.telephony.euicc.EuiccManager;
+import android.util.Log;
import java.lang.ref.WeakReference;
@@ -31,8 +37,26 @@
* the device with no cellular service.
*/
class KeyguardEsimArea extends Button implements View.OnClickListener {
+ private static final String ACTION_DISABLE_ESIM = "com.android.keyguard.disable_esim";
+ private static final String TAG = "KeyguardEsimArea";
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
private EuiccManager mEuiccManager;
+ private BroadcastReceiver mReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_DISABLE_ESIM.equals(intent.getAction())) {
+ int resultCode = getResultCode();
+ if (resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ // TODO (b/62680294): Surface more info. to the end users for this failure.
+ Log.e(TAG, "Error disabling esim, result code = " + resultCode);
+ }
+ }
+ }
+ };
+
public KeyguardEsimArea(Context context) {
this(context, null);
}
@@ -52,8 +76,10 @@
}
@Override
- public void onClick(View v) {
- // STOPSHIP(b/37353596): use EuiccManager API to disable current carrier.
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_DISABLE_ESIM),
+ PERMISSION_SELF, null /* scheduler */);
}
public static boolean isEsimLocked(Context context, int subId) {
@@ -66,4 +92,23 @@
return sub != null && sub.isEmbedded();
}
+ @Override
+ protected void onDetachedFromWindow() {
+ mContext.unregisterReceiver(mReceiver);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(mContext, KeyguardEsimArea.class);
+ intent.setAction(ACTION_DISABLE_ESIM);
+ intent.setPackage(mContext.getPackageName());
+ PendingIntent callbackIntent = PendingIntent.getBroadcast(
+ mContext,
+ 0 /* requestCode */,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mEuiccManager
+ .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2d0fe6f..4d0e60d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -32,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.*;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
@@ -44,6 +45,7 @@
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
+ private boolean mClicked;
public QSTileBaseView(Context context, QSIconView icon) {
this(context, icon, false);
@@ -153,7 +155,11 @@
setContentDescription(state.contentDescription);
mAccessibilityClass = state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
- mTileState = ((QSTile.BooleanState) state).value;
+ boolean newState = ((BooleanState) state).value;
+ if (mTileState != newState) {
+ mClicked = false;
+ mTileState = newState;
+ }
}
}
@@ -173,15 +179,22 @@
}
@Override
+ public boolean performClick() {
+ mClicked = true;
+ return super.performClick();
+ }
+
+ @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
+ boolean b = mClicked ? !mTileState : mTileState;
String label = getResources()
- .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
event.setContentDescription(label);
- event.setChecked(!mTileState);
+ event.setChecked(b);
}
}
}
@@ -192,10 +205,11 @@
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
+ boolean b = mClicked ? !mTileState : mTileState;
String label = getResources()
- .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
info.setText(label);
- info.setChecked(mTileState);
+ info.setChecked(b);
info.setCheckable(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index cb9093a..7391509 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -32,17 +32,16 @@
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.R.string;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
-import com.android.systemui.qs.QSHost;
import com.android.systemui.plugins.qs.QSIconView;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.QSDetailItems;
+import com.android.systemui.qs.QSDetailItems.Item;
+import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.SignalTileView;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -188,7 +187,7 @@
minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc));
}
}
- state.contentDescription = minimalContentDescription;
+ state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a7b845e..a2409d1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -50,6 +50,7 @@
import android.os.UserHandle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
+import android.util.Slog;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -100,6 +101,7 @@
* An AsyncTask that saves an image to the media store in the background.
*/
class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
+ private static final String TAG = "SaveImageInBackgroundTask";
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
@@ -303,6 +305,7 @@
} catch (Exception e) {
// IOException/UnsupportedOperationException may be thrown if external storage is not
// mounted
+ Slog.e(TAG, "unable to save screenshot", e);
mParams.clearImage();
mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
}
@@ -379,8 +382,6 @@
* An AsyncTask that deletes an image from the media store in the background.
*/
class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
- private static final String TAG = "DeleteImageInBackgroundTask";
-
private Context mContext;
DeleteImageInBackgroundTask(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
new file mode 100644
index 0000000..741f783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFrame.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static android.view.MotionEvent.ACTION_OUTSIDE;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+
+import com.android.systemui.statusbar.policy.DeadZone;
+
+public class NavigationBarFrame extends FrameLayout {
+
+ private DeadZone mDeadZone = null;
+
+ public NavigationBarFrame(@NonNull Context context) {
+ super(context);
+ }
+
+ public NavigationBarFrame(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setDeadZone(@NonNull DeadZone deadZone) {
+ mDeadZone = deadZone;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (event.getAction() == ACTION_OUTSIDE) {
+ if (mDeadZone != null) {
+ return mDeadZone.onTouchEvent(event);
+ }
+ }
+ return super.dispatchTouchEvent(event);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index cb3222d6d..a6cd472 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -249,9 +249,6 @@
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
- if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- mDeadZone.poke(event);
- }
return super.onTouchEvent(event);
}
@@ -612,9 +609,8 @@
public void reorient() {
updateCurrentView();
- getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
+ ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
mDeadZone.setDisplayRotation(mCurrentRotation);
// force the low profile & disabled states into compliance
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 193077f..305cf0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -48,6 +48,7 @@
import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.TaskStackBuilder;
+import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -103,7 +104,6 @@
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
@@ -139,6 +139,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
@@ -213,7 +214,6 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.RowInflaterTask;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -309,6 +309,7 @@
public static final boolean DEBUG_GESTURES = false;
public static final boolean DEBUG_MEDIA = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
+ public static final boolean DEBUG_CAMERA_LIFT = true; // false once b/62623620 is fixed
public static final boolean DEBUG_WINDOW_STATE = false;
@@ -1300,10 +1301,6 @@
}
public void onOverlayChanged() {
- final boolean usingDarkTheme = isUsingDarkTheme();
- if (DEBUG) {
- Log.d(TAG, "Updating theme because overlay changed. Is theme dark? " + usingDarkTheme);
- }
reevaluateStyles();
// Clock and bottom icons
@@ -2843,6 +2840,17 @@
updateTheme();
}
+ public boolean isUsingDarkText() {
+ OverlayInfo themeInfo = null;
+ try {
+ themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.lightwallpaper",
+ mCurrentUserId);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return themeInfo != null && themeInfo.isEnabled();
+ }
+
public boolean isUsingDarkTheme() {
OverlayInfo themeInfo = null;
try {
@@ -4521,30 +4529,49 @@
* Switches theme from light to dark and vice-versa.
*/
private void updateTheme() {
- boolean useDarkTheme;
- // Ignore visibility since we calculate the theme based on the real colors,
- // not the current state.
+
+ int which;
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- useDarkTheme = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, true /* vis */)
- .supportsDarkText();
+ which = WallpaperManager.FLAG_LOCK;
} else {
- useDarkTheme = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, true /* vis */)
- .supportsDarkText();
+ which = WallpaperManager.FLAG_SYSTEM;
}
- // Enable/Disable dark overlay
- if (isUsingDarkTheme() != useDarkTheme) {
- if (DEBUG) {
- Log.d(TAG, "Switching theme to: " + (useDarkTheme ? "Dark" : "Light"));
+ // Gradient defines if text color should be light or dark.
+ final boolean useDarkText = mColorExtractor.getColors(which, true /* ignoreVisibility */)
+ .supportsDarkText();
+ // And wallpaper defines if QS should be light or dark.
+ boolean useDarkTheme = false;
+ final WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
+ if (wallpaperManager != null) {
+ WallpaperColors wallpaperColors = wallpaperManager
+ .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+ if (wallpaperColors != null) {
+ final int mainColor = wallpaperColors.getPrimaryColor().toArgb();
+ final float[] hsl = new float[3];
+ ColorUtils.colorToHSL(mainColor, hsl);
+ useDarkTheme = hsl[2] < 0.2f;
}
+ }
+
+ // Enable/disable dark UI.
+ if (isUsingDarkTheme() != useDarkTheme) {
try {
mOverlayManager.setEnabled("com.android.systemui.theme.dark",
useDarkTheme, mCurrentUserId);
} catch (RemoteException e) {
Log.w(TAG, "Can't change theme", e);
- return;
}
- mStatusBarWindowManager.setKeyguardDark(useDarkTheme);
+ }
+ // Enable/disable dark text overlay.
+ if (isUsingDarkText() != useDarkText) {
+ try {
+ mOverlayManager.setEnabled("com.android.systemui.theme.lightwallpaper",
+ useDarkText, mCurrentUserId);
+ mStatusBarWindowManager.setKeyguardDark(useDarkText);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't change theme", e);
+ }
}
}
@@ -5163,11 +5190,14 @@
public void onCameraLaunchGestureDetected(int source) {
mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera");
mLaunchCameraOnFinishedGoingToSleep = true;
return;
}
if (!mNotificationPanel.canCameraGestureBeLaunched(
mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now, mExpandedVisible: " +
+ mExpandedVisible);
return;
}
if (!mDeviceInteractive) {
@@ -5187,12 +5217,14 @@
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
+ if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
mLaunchCameraOnScreenTurningOn = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
index 4c879c6..13ee23f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java
@@ -127,6 +127,7 @@
final int action = event.getAction();
if (action == MotionEvent.ACTION_OUTSIDE) {
poke(event);
+ return true;
} else if (action == MotionEvent.ACTION_DOWN) {
if (DEBUG) {
Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY());
@@ -158,7 +159,7 @@
return false;
}
- public void poke(MotionEvent event) {
+ private void poke(MotionEvent event) {
mLastPokeTime = event.getEventTime();
if (DEBUG)
Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index a120cec..4cc83f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -14,7 +14,6 @@
package com.android.systemui.statusbar.phone;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -33,17 +32,13 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.utils.leaks.BaseLeakChecker;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -54,6 +49,10 @@
super(NavigationBarFragment.class);
}
+ protected void createRootView() {
+ mView = new NavigationBarFrame(mContext);
+ }
+
@Before
public void setup() {
mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
diff --git a/packages/SysuiDarkThemeOverlay/Android.mk b/packages/overlays/SysuiDarkThemeOverlay/Android.mk
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/Android.mk
rename to packages/overlays/SysuiDarkThemeOverlay/Android.mk
diff --git a/packages/SysuiDarkThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/AndroidManifest.xml
rename to packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
diff --git a/packages/SysuiDarkThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
similarity index 100%
rename from packages/SysuiDarkThemeOverlay/res/values/strings.xml
rename to packages/overlays/SysuiDarkThemeOverlay/res/values/strings.xml
diff --git a/packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
similarity index 72%
rename from packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
rename to packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
index 28ecfa0..7e2b955 100644
--- a/packages/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
+++ b/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <!-- Dark theme for a window that should look like the Settings app. -->
<style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault">
- <!-- Color palette -->
<item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
<item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
- <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
- <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ <!-- textColorPrimaryInverse is used on the lock screen and this means that we can't just
+ invert text colors otherwise we won't have contrast on the keyguard -->
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_dark</item>
+ <!-- same for textColorSecondaryInverse -->
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_dark</item>
<item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
<item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
<item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk b/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk
new file mode 100644
index 0000000..4782a16
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := SysuiLightWallpaperTheme
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := SysuiLightWallpaperThemeOverlay
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..1745b4c
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.theme.lightwallpaper"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="android" android:priority="2"/>
+
+ <application android:label="@string/sysui_overlay_light" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml
new file mode 100644
index 0000000..acc3d16
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <string name="sysui_overlay_light">Light</string>
+
+</resources>
+
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
new file mode 100644
index 0000000..877ebf8
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault.Light">
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index fa2b1ee..baa135d 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4095,6 +4095,15 @@
// OS: O DR
CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR = 1013;
+ // OPEN: Settings > Network > Tether > Wi-Fi hotspot
+ WIFI_TETHER_SETTINGS = 1014;
+
+ // OPEN: Settings->Connected Devices->Bluetooth->(click on details link for a paired device)
+ // -> Edit name button.
+ // CATEGORY: SETTINGS
+ // OS: O DR
+ DIALOG_BLUETOOTH_PAIRED_DEVICE_RENAME = 1015;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b408da8..4c9495a 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,7 +17,6 @@
package com.android.server;
import android.app.ActivityManager;
-import android.app.KeyguardManager;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -41,10 +40,12 @@
import android.util.MutableBoolean;
import android.util.Slog;
import android.view.KeyEvent;
+import android.view.WindowManagerInternal;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -56,6 +57,7 @@
*/
public class GestureLauncherService extends SystemService {
private static final boolean DBG = false;
+ private static final boolean DBG_CAMERA_LIFT = true; // false once b/62623620 is fixed
private static final String TAG = "GestureLauncherService";
/**
@@ -82,7 +84,7 @@
private Context mContext;
private final MetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
- private KeyguardManager mKeyguardManager;
+ private WindowManagerInternal mWindowManagerInternal;
/** The wake lock held when a gesture is detected. */
private WakeLock mWakeLock;
@@ -150,8 +152,7 @@
return;
}
- mKeyguardManager = (KeyguardManager) mContext.getSystemService(
- Context.KEYGUARD_SERVICE);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPowerManager = (PowerManager) mContext.getSystemService(
Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -517,33 +518,40 @@
private final class CameraLiftTriggerEventListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent event) {
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, String.format("onTrigger event - time: %d, name: %s",
+ event.timestamp, event.sensor.getName()));
if (!mCameraLiftRegistered) {
- if (DBG) Slog.d(TAG, "Ignoring camera lift event because it's unregistered.");
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring camera lift event because it's " +
+ "unregistered.");
return;
}
if (event.sensor == mCameraLiftTriggerSensor) {
Resources resources = mContext.getResources();
SensorManager sensorManager = (SensorManager) mContext.getSystemService(
Context.SENSOR_SERVICE);
-
- if (DBG) {
+ boolean keyguardShowingAndNotOccluded =
+ mWindowManagerInternal.isKeyguardShowingAndNotOccluded();
+ boolean interactive = mPowerManager.isInteractive();
+ if (DBG_CAMERA_LIFT) {
float[] values = event.values;
- Slog.d(TAG, String.format("Received a camera lift trigger event: " +
- "values=[%.4f].", values[0]));
+ Slog.d(TAG, String.format("Received a camera lift trigger " +
+ "event: values=[%.4f], keyguard showing: %b, interactive: %b", values[0],
+ keyguardShowingAndNotOccluded, interactive));
}
- if (mKeyguardManager.isKeyguardLocked() || !mPowerManager.isInteractive()) {
+ if (keyguardShowingAndNotOccluded || !interactive) {
if (handleCameraGesture(true /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_CAMERA_LIFT_TRIGGER);
}
- } else if (DBG) {
- Slog.d(TAG, "Ignoring lift event because device is awake");
+ } else {
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Ignoring lift event");
}
- mCameraLiftRegistered = sensorManager.requestTriggerSensor(mCameraLiftTriggerListener,
- mCameraLiftTriggerSensor);
+ mCameraLiftRegistered = sensorManager.requestTriggerSensor(
+ mCameraLiftTriggerListener, mCameraLiftTriggerSensor);
- if (DBG) Slog.d(TAG, "Camera lift trigger sensor re-registered: " + mCameraLiftRegistered);
+ if (DBG_CAMERA_LIFT) Slog.d(TAG, "Camera lift trigger sensor re-registered: " +
+ mCameraLiftRegistered);
return;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5d5614c..957b406 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2273,6 +2273,31 @@
return null;
}
+ /**
+ * Get next valid stack for launching provided activity in the system. This will search across
+ * displays and stacks in last-focused order for a focusable and visible stack, except those
+ * that are on a currently focused display.
+ *
+ * @param r The activity that is being launched.
+ * @param currentFocus The display that previously had focus and thus needs to be ignored when
+ * searching for the next candidate.
+ * @return Next valid {@link ActivityStack}, null if not found.
+ */
+ ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+ for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
+ final int displayId = mTmpOrderedDisplayIds.get(i);
+ if (displayId == currentFocus) {
+ continue;
+ }
+ final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r);
+ if (stack != null) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
ActivityRecord getHomeActivity() {
return getHomeActivityForUser(mCurrentUser);
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d74d1d6..c7ee149 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -2027,7 +2027,18 @@
return mSupervisor.mFocusedStack;
}
- if (mSourceDisplayId == DEFAULT_DISPLAY) {
+ if (mSourceDisplayId != DEFAULT_DISPLAY) {
+ // Try to put the activity in a stack on a secondary display.
+ stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
+ if (stack == null) {
+ // If source display is not suitable - look for topmost valid stack in the system.
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId
+ + ", looking on all displays.");
+ stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId);
+ }
+ }
+ if (stack == null) {
// We first try to put the task in the first dynamic stack on home display.
final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
@@ -2043,8 +2054,6 @@
bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
FULLSCREEN_WORKSPACE_STACK_ID;
stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
- } else {
- stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r);
}
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ r + " stackId=" + stack.mStackId);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 37ef132..3daaf8f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1257,10 +1257,10 @@
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
}
- setUpstreamByType(ns);
+ setUpstreamNetwork(ns);
}
- protected void setUpstreamByType(NetworkState ns) {
+ protected void setUpstreamNetwork(NetworkState ns) {
String iface = null;
if (ns != null && ns.linkProperties != null) {
// Find the interface with the default IPv4 route. It may be the
@@ -1779,7 +1779,9 @@
}
}
- mLog.log(String.format("OBSERVED LinkProperties update iface=%s state=%s", iface, state));
+ mLog.log(String.format(
+ "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
+ iface, IControlsTethering.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
index aaa63b1..2b81347 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
@@ -33,6 +33,16 @@
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
+ public static String getStateString(int state) {
+ switch (state) {
+ case STATE_UNAVAILABLE: return "UNAVAILABLE";
+ case STATE_AVAILABLE: return "AVAILABLE";
+ case STATE_TETHERED: return "TETHERED";
+ case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
+ }
+ return "UNKNOWN: " + state;
+ }
+
/**
* Notify that |who| has changed its tethering state.
*
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index ce6b8be..08deef8 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -85,13 +85,16 @@
mLog.i("tethering offload control not supported");
stop();
}
+ mLog.log("tethering offload started");
}
public void stop() {
+ final boolean wasStarted = started();
mUpstreamLinkProperties = null;
mHwInterface.stopOffloadControl();
mControlInitialized = false;
mConfigInitialized = false;
+ if (wasStarted) mLog.log("tethering offload stopped");
}
public void setUpstreamLinkProperties(LinkProperties lp) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index b88361f..881c0ca 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -56,9 +56,10 @@
import java.util.Random;
/**
- * @hide
+ * Provides the interface to IP-layer serving functionality for a given network
+ * interface, e.g. for tethering or "local-only hotspot" mode.
*
- * Tracks the eligibility of a given network interface for tethering.
+ * @hide
*/
public class TetherInterfaceStateMachine extends StateMachine {
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
@@ -117,6 +118,12 @@
private String mMyUpstreamIfaceName; // may change over time
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
+ // TODO: De-duplicate this with mLinkProperties above. Currently, these link
+ // properties are those selected by the IPv6TetheringCoordinator and relayed
+ // to us. By comparison, mLinkProperties contains the addresses and directly
+ // connected routes that have been formed from these properties iff. we have
+ // succeeded in configuring them and are able to announce them within Router
+ // Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -133,7 +140,7 @@
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mIfaceName);
+ resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mInitialState = new InitialState();
@@ -160,10 +167,15 @@
* Internals.
*/
- // configured when we start tethering and unconfig'd on error or conclusion
- private boolean configureIfaceIp(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
+ private boolean startIPv4() { return configureIPv4(true); }
+ private void stopIPv4() { configureIPv4(false); }
+
+ private boolean configureIPv4(boolean enabled) {
+ if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
+
+ // TODO: Replace this hard-coded information with dynamically selected
+ // config passed down to us by a higher layer IP-coordinating element.
String ipAsString = null;
int prefixLen = 0;
if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
@@ -177,32 +189,45 @@
return true;
}
- InterfaceConfiguration ifcg = null;
+ final LinkAddress linkAddr;
try {
- ifcg = mNMService.getInterfaceConfig(mIfaceName);
- if (ifcg != null) {
- InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
- ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
- if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
- // The WiFi stack has ownership of the interface up/down state.
- // It is unclear whether the bluetooth or USB stacks will manage their own
- // state.
- ifcg.ignoreInterfaceUpDownStatus();
- } else {
- if (enabled) {
- ifcg.setInterfaceUp();
- } else {
- ifcg.setInterfaceDown();
- }
- }
- ifcg.clearFlag("running");
- mNMService.setInterfaceConfig(mIfaceName, ifcg);
+ final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
+ if (ifcg == null) {
+ mLog.e("Received null interface config");
+ return false;
}
+
+ InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
+ linkAddr = new LinkAddress(addr, prefixLen);
+ ifcg.setLinkAddress(linkAddr);
+ if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
+ // The WiFi stack has ownership of the interface up/down state.
+ // It is unclear whether the Bluetooth or USB stacks will manage their own
+ // state.
+ ifcg.ignoreInterfaceUpDownStatus();
+ } else {
+ if (enabled) {
+ ifcg.setInterfaceUp();
+ } else {
+ ifcg.setInterfaceDown();
+ }
+ }
+ ifcg.clearFlag("running");
+ mNMService.setInterfaceConfig(mIfaceName, ifcg);
} catch (Exception e) {
mLog.e("Error configuring interface " + e);
return false;
}
+ // Directly-connected route.
+ final RouteInfo route = new RouteInfo(linkAddr);
+ if (enabled) {
+ mLinkProperties.addLinkAddress(linkAddr);
+ mLinkProperties.addRoute(route);
+ } else {
+ mLinkProperties.removeLinkAddress(linkAddr);
+ mLinkProperties.removeRoute(route);
+ }
return true;
}
@@ -292,7 +317,7 @@
mLastIPv6LinkProperties = v6only;
}
- private void configureLocalRoutes(
+ private void configureLocalIPv6Routes(
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
// [1] Remove the routes that are deprecated.
if (!deprecatedPrefixes.isEmpty()) {
@@ -307,6 +332,8 @@
} catch (RemoteException e) {
mLog.e("Failed to remove IPv6 routes from local table: " + e);
}
+
+ for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
}
// [2] Add only the routes that have not previously been added.
@@ -338,11 +365,13 @@
} catch (RemoteException e) {
mLog.e("Failed to add IPv6 routes to local table: " + e);
}
+
+ for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
}
}
}
- private void configureLocalDns(
+ private void configureLocalIPv6Dns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
final INetd netd = NetdService.getInstance();
if (netd == null) {
@@ -360,6 +389,8 @@
} catch (ServiceSpecificException | RemoteException e) {
mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
}
+
+ mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -378,6 +409,8 @@
mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
newDnses.remove(dns);
}
+
+ mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -394,10 +427,10 @@
final RaParams deprecatedParams =
RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
- configureLocalRoutes(deprecatedParams.prefixes,
+ configureLocalIPv6Routes(deprecatedParams.prefixes,
(newParams != null) ? newParams.prefixes : null);
- configureLocalDns(deprecatedParams.dnses,
+ configureLocalIPv6Dns(deprecatedParams.dnses,
(newParams != null) ? newParams.dnses : null);
mRaDaemon.buildNewRa(deprecatedParams, newParams);
@@ -417,12 +450,19 @@
private void sendInterfaceState(int newInterfaceState) {
mTetherController.updateInterfaceState(
TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
- // TODO: Populate mLinkProperties correctly, and send more sensible
- // updates more frequently (not just here).
+ sendLinkProperties();
+ }
+
+ private void sendLinkProperties() {
mTetherController.updateLinkProperties(
TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
}
+ private void resetLinkProperties() {
+ mLinkProperties.clear();
+ mLinkProperties.setInterfaceName(mIfaceName);
+ }
+
class InitialState extends State {
@Override
public void enter() {
@@ -462,7 +502,7 @@
class BaseServingState extends State {
@Override
public void enter() {
- if (!configureIfaceIp(true)) {
+ if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -496,7 +536,9 @@
mLog.e("Failed to untether interface: " + e);
}
- configureIfaceIp(false);
+ stopIPv4();
+
+ resetLinkProperties();
}
@Override
@@ -513,6 +555,7 @@
break;
case CMD_IPV6_TETHER_UPDATE:
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+ sendLinkProperties();
break;
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -623,7 +666,6 @@
if (super.processMessage(message)) return true;
maybeLogMessage(this, message.what);
- boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
@@ -653,10 +695,9 @@
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
default:
- retValue = false;
- break;
+ return false;
}
- return retValue;
+ return true;
}
}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 5d3f6f7..107475f 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -438,7 +438,21 @@
switch (message.what) {
case MSG_TIMEOUT:
synchronized (mLock) {
- handleOpTimeoutLocked();
+ if (message.obj == mRunningCallback) {
+ handleOpTimeoutLocked();
+ } else {
+ JobCallback jc = (JobCallback)message.obj;
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Ignoring timeout of no longer active job");
+ if (jc.mStoppedReason != null) {
+ sb.append(", stopped ");
+ TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+ - jc.mStoppedTime, sb);
+ sb.append(" because: ");
+ sb.append(jc.mStoppedReason);
+ }
+ Slog.w(TAG, sb.toString());
+ }
}
break;
default:
@@ -621,7 +635,7 @@
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
- Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+ Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
break;
@@ -629,26 +643,28 @@
// Client unresponsive - wedged or failed to respond in time. We don't really
// know what happened so let's log it and notify the JobScheduler
// FINISHED/NO-RETRY.
- Slog.e(TAG, "No response from client for onStartJob '" +
- mRunningJob.toShortString());
+ Slog.w(TAG, "No response from client for onStartJob " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
break;
case VERB_STOPPING:
// At least we got somewhere, so fail but ask the JobScheduler to reschedule.
- Slog.e(TAG, "No response from client for onStopJob, '" +
- mRunningJob.toShortString());
+ Slog.w(TAG, "No response from client for onStopJob " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
break;
case VERB_EXECUTING:
// Not an error - client ran out of time.
- Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
- " sending onStop. " + mRunningJob.toShortString());
+ Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
+ "sending onStop: " +
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>");
mParams.setStopReason(JobParameters.REASON_TIMEOUT);
sendStopMessageLocked("timeout while executing");
break;
default:
Slog.e(TAG, "Handling timeout for an invalid job state: " +
- mRunningJob.toShortString() + ", dropping.");
+ mRunningJob != null ? mRunningJob.toShortString() : "<null>"
+ + ", dropping.");
closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
}
}
@@ -749,7 +765,7 @@
mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
}
- Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+ Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index b3c6ff6..d7b36aa 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -525,12 +525,11 @@
if (r == null) {
throw new IllegalArgumentException("Invalid package");
}
- LogMaker lm = new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
- .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
- .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
- group.getId())
- .setPackageName(pkg);
- MetricsLogger.action(lm);
+ final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+ if (!group.equals(oldGroup)) {
+ // will log for new entries as well as name changes
+ MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+ }
r.groups.put(group.getId(), group);
updateConfig();
}
@@ -558,13 +557,16 @@
if (existing != null && fromTargetApp) {
if (existing.isDeleted()) {
existing.setDeleted(false);
+
+ // log a resurrected channel as if it's new again
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ MetricsProto.MetricsEvent.TYPE_OPEN));
}
existing.setName(channel.getName().toString());
existing.setDescription(channel.getDescription());
existing.setBlockableSystem(channel.isBlockableSystem());
- MetricsLogger.action(getChannelLog(channel, pkg));
updateConfig();
return;
}
@@ -622,7 +624,10 @@
r.showBadge = updatedChannel.canShowBadge();
}
- MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+ if (!channel.equals(updatedChannel)) {
+ // only log if there are real changes
+ MetricsLogger.action(getChannelLog(updatedChannel, pkg));
+ }
updateConfig();
}
@@ -1141,6 +1146,14 @@
channel.getImportance());
}
+ private LogMaker getChannelGroupLog(String groupId, String pkg) {
+ return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
+ .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+ groupId)
+ .setPackageName(pkg);
+ }
+
public void updateBadgingEnabled() {
if (mBadgingEnabled == null) {
mBadgingEnabled = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a032844..5c6521b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7713,6 +7713,8 @@
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
default:
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f86534b..7944e68 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3040,6 +3040,10 @@
return mPolicy.isKeyguardLocked();
}
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return mPolicy.isKeyguardShowingAndNotOccluded();
+ }
+
@Override
public boolean isKeyguardSecure() {
int userId = UserHandle.getCallingUserId();
@@ -7291,6 +7295,11 @@
}
@Override
+ public boolean isKeyguardShowingAndNotOccluded() {
+ return WindowManagerService.this.isKeyguardShowingAndNotOccluded();
+ }
+
+ @Override
public void showGlobalActions() {
WindowManagerService.this.showGlobalActions();
}
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 75df892..5770c50 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -159,10 +159,12 @@
readInstalledPrintServicesLocked();
upgradePersistentStateIfNeeded();
readDisabledPrintServicesLocked();
+ }
- // Some print services might have gotten installed before the User State came up
- prunePrintServices();
+ // Some print services might have gotten installed before the User State came up
+ prunePrintServices();
+ synchronized (mLock) {
onConfigurationChangedLocked();
}
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 14284d6..1ddaf66 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -62,7 +62,8 @@
@Mock private OffloadHardwareInterface mHardware;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class);
+ private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
private MockContentResolver mContentResolver;
@Before public void setUp() throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index ce419a5..db5373a 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.tethering;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -40,17 +42,23 @@
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.RouteInfo;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+
+import java.net.Inet4Address;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -69,6 +77,8 @@
@Mock private SharedLog mSharedLog;
private final TestLooper mLooper = new TestLooper();
+ private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
private TetherInterfaceStateMachine mTestedSm;
private void initStateMachine(int interfaceType) throws Exception {
@@ -77,7 +87,7 @@
mNMService, mStatsService, mTetherHelper);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
- // the test of the world that we've changed from an unknown to available state.
+ // the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
reset(mNMService, mStatsService, mTetherHelper);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
@@ -181,7 +191,8 @@
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
}
@@ -281,7 +292,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -298,7 +310,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -313,7 +326,8 @@
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -360,4 +374,28 @@
upstreamIface);
mLooper.dispatchAll();
}
+
+ private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
+ // Find the first IPv4 LinkAddress.
+ LinkAddress addr4 = null;
+ for (LinkAddress addr : lp.getLinkAddresses()) {
+ if (!(addr.getAddress() instanceof Inet4Address)) continue;
+ addr4 = addr;
+ break;
+ }
+ assertTrue("missing IPv4 address", addr4 != null);
+
+ // Assert the presence of the associated directly connected route.
+ final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+ assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
+ lp.getRoutes().contains(directlyConnected));
+ }
+
+ private void assertNoAddressesNorRoutes(LinkProperties lp) {
+ assertTrue(lp.getLinkAddresses().isEmpty());
+ assertTrue(lp.getRoutes().isEmpty());
+ // We also check that interface name is non-empty, because we should
+ // never see an empty interface name in any LinkProperties update.
+ assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
+ }
}
diff --git a/tests/testables/src/android/testing/BaseFragmentTest.java b/tests/testables/src/android/testing/BaseFragmentTest.java
index 5cedbdf..5fa065a 100644
--- a/tests/testables/src/android/testing/BaseFragmentTest.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -50,7 +50,7 @@
private static final int VIEW_ID = 42;
private final Class<? extends Fragment> mCls;
private Handler mHandler;
- private FrameLayout mView;
+ protected FrameLayout mView;
protected FragmentController mFragments;
protected Fragment mFragment;
@@ -61,9 +61,13 @@
mCls = cls;
}
+ protected void createRootView() {
+ mView = new FrameLayout(mContext);
+ }
+
@Before
public void setupFragment() throws Exception {
- mView = new FrameLayout(mContext);
+ createRootView();
mView.setId(VIEW_ID);
assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",