Merge "Fix system crash in certain jobs-removed cases"
diff --git a/Android.mk b/Android.mk
index 10d11f3..9676958 100644
--- a/Android.mk
+++ b/Android.mk
@@ -486,6 +486,10 @@
include $(BUILD_APIDIFF)
+# Hack to get diffs included in docs output
+out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
+$(out_zip): $(full_target)
+
# ==== System API diff ===========================
include $(CLEAR_VARS)
@@ -512,6 +516,10 @@
include $(BUILD_APIDIFF)
+# Hack to get diffs included in docs output
+out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
+$(out_zip): $(full_target)
+
# ==== the api stubs and current.xml ===========================
include $(CLEAR_VARS)
@@ -656,10 +664,6 @@
# Check comment when you are updating the API
update-api: doc-comment-check-docs
-# Generate API diffs as part of docs builds
-docs: offline-sdk-referenceonly-diff
-docs: offline-system-sdk-referenceonly-diff
-
# ==== static html in the sdk ==================================
include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index 0d55ddf..c3dbee0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6394,7 +6394,7 @@
method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
- method public boolean isLogoutButtonEnabled();
+ method public boolean isLogoutEnabled();
method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
@@ -6438,7 +6438,7 @@
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
- method public void setLogoutButtonEnabled(android.content.ComponentName, boolean);
+ method public void setLogoutEnabled(android.content.ComponentName, boolean);
method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
@@ -20559,6 +20559,7 @@
method public int getMaxWidth();
method public java.lang.CharSequence getTextForImeAction(int);
method public android.app.Dialog getWindow();
+ method public void hideSoftInputFromInputMethod(int);
method public void hideStatusIcon();
method public void hideWindow();
method public boolean isExtractViewShown();
@@ -20614,10 +20615,16 @@
method public void setCandidatesViewShown(boolean);
method public void setExtractView(android.view.View);
method public void setExtractViewShown(boolean);
+ method public void setInputMethod(java.lang.String);
+ method public void setInputMethodAndSubtype(java.lang.String, android.view.inputmethod.InputMethodSubtype);
method public void setInputView(android.view.View);
+ method public boolean shouldOfferSwitchingToNextInputMethod();
+ method public void showSoftInputFromInputMethod(int);
method public void showStatusIcon(int);
method public void showWindow(boolean);
method public void switchInputMethod(java.lang.String);
+ method public boolean switchToLastInputMethod();
+ method public boolean switchToNextInputMethod(boolean);
method public void updateFullscreenMode();
method public void updateInputViewShown();
field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0
@@ -49035,10 +49042,10 @@
method public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodList();
method public android.view.inputmethod.InputMethodSubtype getLastInputMethodSubtype();
method public java.util.Map<android.view.inputmethod.InputMethodInfo, java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes();
- method public void hideSoftInputFromInputMethod(android.os.IBinder, int);
+ method public deprecated void hideSoftInputFromInputMethod(android.os.IBinder, int);
method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
- method public void hideStatusIcon(android.os.IBinder);
+ method public deprecated void hideStatusIcon(android.os.IBinder);
method public boolean isAcceptingText();
method public boolean isActive(android.view.View);
method public boolean isActive();
@@ -49048,17 +49055,17 @@
method public void sendAppPrivateCommand(android.view.View, java.lang.String, android.os.Bundle);
method public void setAdditionalInputMethodSubtypes(java.lang.String, android.view.inputmethod.InputMethodSubtype[]);
method public boolean setCurrentInputMethodSubtype(android.view.inputmethod.InputMethodSubtype);
- method public void setInputMethod(android.os.IBinder, java.lang.String);
- method public void setInputMethodAndSubtype(android.os.IBinder, java.lang.String, android.view.inputmethod.InputMethodSubtype);
- method public boolean shouldOfferSwitchingToNextInputMethod(android.os.IBinder);
+ method public deprecated void setInputMethod(android.os.IBinder, java.lang.String);
+ method public deprecated void setInputMethodAndSubtype(android.os.IBinder, java.lang.String, android.view.inputmethod.InputMethodSubtype);
+ method public deprecated boolean shouldOfferSwitchingToNextInputMethod(android.os.IBinder);
method public void showInputMethodAndSubtypeEnabler(java.lang.String);
method public void showInputMethodPicker();
method public boolean showSoftInput(android.view.View, int);
method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
- method public void showSoftInputFromInputMethod(android.os.IBinder, int);
- method public void showStatusIcon(android.os.IBinder, java.lang.String, int);
- method public boolean switchToLastInputMethod(android.os.IBinder);
- method public boolean switchToNextInputMethod(android.os.IBinder, boolean);
+ method public deprecated void showSoftInputFromInputMethod(android.os.IBinder, int);
+ method public deprecated void showStatusIcon(android.os.IBinder, java.lang.String, int);
+ method public deprecated boolean switchToLastInputMethod(android.os.IBinder);
+ method public deprecated boolean switchToNextInputMethod(android.os.IBinder, boolean);
method public void toggleSoftInput(int, int);
method public void toggleSoftInputFromWindow(android.os.IBinder, int, int);
method public deprecated void updateCursor(android.view.View, int, int, int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 3c3521f..27e585d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -162,6 +162,10 @@
method public abstract boolean isPermissionReviewModeEnabled();
}
+ public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+ }
+
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7a1931718..037aeb0 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -35,23 +35,23 @@
void setServiceInfo(in AccessibilityServiceInfo info);
- boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
in Bundle arguments);
- boolean findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
+ String[] findAccessibilityNodeInfosByText(int accessibilityWindowId, long accessibilityNodeId,
String text, int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId);
- boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
- boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
- boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId);
boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
diff --git a/core/java/android/annotation/IntDef.java b/core/java/android/annotation/IntDef.java
index 3f9064e..f84a676 100644
--- a/core/java/android/annotation/IntDef.java
+++ b/core/java/android/annotation/IntDef.java
@@ -52,10 +52,12 @@
@Target({ANNOTATION_TYPE})
public @interface IntDef {
/** Defines the constant prefix for this element */
- String[] prefix() default "";
+ String[] prefix() default {};
+ /** Defines the constant suffix for this element */
+ String[] suffix() default {};
/** Defines the allowed constants for this element */
- long[] value() default {};
+ int[] value() default {};
/** Defines whether the constants can be used as a flag, or just as an enum (the default) */
boolean flag() default false;
diff --git a/core/java/android/annotation/LongDef.java b/core/java/android/annotation/LongDef.java
new file mode 100644
index 0000000..8723eef8
--- /dev/null
+++ b/core/java/android/annotation/LongDef.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Denotes that the annotated long element represents
+ * a logical type and that its value should be one of the explicitly
+ * named constants. If the {@link #flag()} attribute is set to true,
+ * multiple constants can be combined.
+ * <p>
+ * <pre><code>
+ * @Retention(SOURCE)
+ * @LongDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ * public @interface NavigationMode {}
+ * public static final long NAVIGATION_MODE_STANDARD = 0;
+ * public static final long NAVIGATION_MODE_LIST = 1;
+ * public static final long NAVIGATION_MODE_TABS = 2;
+ * ...
+ * public abstract void setNavigationMode(@NavigationMode long mode);
+ * @NavigationMode
+ * public abstract long getNavigationMode();
+ * </code></pre>
+ * For a flag, set the flag attribute:
+ * <pre><code>
+ * @LongDef(
+ * flag = true,
+ * value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
+ * </code></pre>
+ *
+ * @hide
+ */
+@Retention(SOURCE)
+@Target({ANNOTATION_TYPE})
+public @interface LongDef {
+ /** Defines the constant prefix for this element */
+ String[] prefix() default "";
+
+ /** Defines the allowed constants for this element */
+ long[] value() default {};
+
+ /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
+ boolean flag() default false;
+}
diff --git a/core/java/android/annotation/StringDef.java b/core/java/android/annotation/StringDef.java
index d5157c3..a37535b 100644
--- a/core/java/android/annotation/StringDef.java
+++ b/core/java/android/annotation/StringDef.java
@@ -46,6 +46,11 @@
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface StringDef {
+ /** Defines the constant prefix for this element */
+ String[] prefix() default {};
+ /** Defines the constant suffix for this element */
+ String[] suffix() default {};
+
/** Defines the allowed constants for this element */
String[] value() default {};
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f831ae2..495fd3c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -618,12 +618,13 @@
ConnectivityThread.getInstanceLooper());
}});
- registerService(Context.WIFI_RTT2_SERVICE, WifiRttManager.class,
+ registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
@Override
public WifiRttManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT2_SERVICE);
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.WIFI_RTT_RANGING_SERVICE);
IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
return new WifiRttManager(ctx.getOuterContext(), service);
}});
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 562b981..ad21983 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8585,32 +8585,31 @@
}
/**
- * Called by a device owner to specify whether a logout button is enabled for all secondary
- * users. The system may show a logout button that stops the user and switches back to the
- * primary user.
+ * Called by a device owner to specify whether logout is enabled for all secondary users. The
+ * system may show a logout button that stops the user and switches back to the primary user.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param enabled whether logout button should be enabled or not.
+ * @param enabled whether logout should be enabled or not.
* @throws SecurityException if {@code admin} is not a device owner.
*/
- public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) {
- throwIfParentInstance("setLogoutButtonEnabled");
+ public void setLogoutEnabled(@NonNull ComponentName admin, boolean enabled) {
+ throwIfParentInstance("setLogoutEnabled");
try {
- mService.setLogoutButtonEnabled(admin, enabled);
+ mService.setLogoutEnabled(admin, enabled);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
/**
- * Returns whether logout button is enabled by a device owner.
+ * Returns whether logout is enabled by a device owner.
*
- * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise.
+ * @return {@code true} if logout is enabled by device owner, {@code false} otherwise.
*/
- public boolean isLogoutButtonEnabled() {
- throwIfParentInstance("isLogoutButtonEnabled");
+ public boolean isLogoutEnabled() {
+ throwIfParentInstance("isLogoutEnabled");
try {
- return mService.isLogoutButtonEnabled();
+ return mService.isLogoutEnabled();
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c525df7..ff869d2 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -372,6 +372,6 @@
boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback);
- void setLogoutButtonEnabled(in ComponentName admin, boolean enabled);
- boolean isLogoutButtonEnabled();
+ void setLogoutEnabled(in ComponentName admin, boolean enabled);
+ boolean isLogoutEnabled();
}
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index ddc5760..807c47d 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -37,6 +37,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -53,9 +55,21 @@
/**
* @hide
*/
- @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
- HINT_NO_TINT, HINT_PARTIAL})
- public @interface SliceHint{ }
+ @StringDef(prefix = { "HINT_" }, value = {
+ HINT_TITLE,
+ HINT_LIST,
+ HINT_LIST_ITEM,
+ HINT_LARGE,
+ HINT_ACTIONS,
+ HINT_SELECTED,
+ HINT_NO_TINT,
+ HINT_HIDDEN,
+ HINT_TOGGLE,
+ HINT_HORIZONTAL,
+ HINT_PARTIAL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SliceHint {}
/**
* The meta-data key that allows an activity to easily be linked directly to a slice.
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index cdeee35..743d6b7 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -29,6 +29,8 @@
import com.android.internal.util.ArrayUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
@@ -55,8 +57,16 @@
/**
* @hide
*/
- @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_COLOR,
- FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
+ @StringDef(prefix = { "FORMAT_" }, value = {
+ FORMAT_SLICE,
+ FORMAT_TEXT,
+ FORMAT_IMAGE,
+ FORMAT_ACTION,
+ FORMAT_COLOR,
+ FORMAT_TIMESTAMP,
+ FORMAT_REMOTE_INPUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceType {}
/**
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index ad9b698..417e7d2 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -105,9 +105,9 @@
*/
public RulesState getRulesState() {
try {
- logDebug("sIRulesManager.getRulesState()");
+ logDebug("mIRulesManager.getRulesState()");
RulesState rulesState = mIRulesManager.getRulesState();
- logDebug("sIRulesManager.getRulesState() returned " + rulesState);
+ logDebug("mIRulesManager.getRulesState() returned " + rulesState);
return rulesState;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -131,7 +131,7 @@
ICallback iCallback = new CallbackWrapper(mContext, callback);
try {
- logDebug("sIRulesManager.requestInstall()");
+ logDebug("mIRulesManager.requestInstall()");
return mIRulesManager.requestInstall(distroFileDescriptor, checkToken, iCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -151,7 +151,7 @@
public int requestUninstall(byte[] checkToken, Callback callback) {
ICallback iCallback = new CallbackWrapper(mContext, callback);
try {
- logDebug("sIRulesManager.requestUninstall()");
+ logDebug("mIRulesManager.requestUninstall()");
return mIRulesManager.requestUninstall(checkToken, iCallback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -196,7 +196,7 @@
*/
public void requestNothing(byte[] checkToken, boolean succeeded) {
try {
- logDebug("sIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken));
+ logDebug("mIRulesManager.requestNothing() with token=" + Arrays.toString(checkToken));
mIRulesManager.requestNothing(checkToken, succeeded);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java
new file mode 100644
index 0000000..7ab3d8b
--- /dev/null
+++ b/core/java/android/appwidget/AppWidgetManagerInternal.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.appwidget;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+
+import java.util.Set;
+
+/**
+ * App widget manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class AppWidgetManagerInternal {
+
+ /**
+ * Gets the packages from which the uid hosts widgets.
+ *
+ * @param uid The potential host UID.
+ * @return Whether the UID hosts widgets from the package.
+ */
+ public abstract @Nullable ArraySet<String> getHostedWidgetPackages(int uid);
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9800ab0..a474330 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2913,7 +2913,7 @@
@Nullable String profileFile, @Nullable Bundle arguments);
/** @hide */
- @StringDef({
+ @StringDef(suffix = { "_SERVICE" }, value = {
POWER_SERVICE,
WINDOW_SERVICE,
LAYOUT_INFLATER_SERVICE,
@@ -2947,7 +2947,7 @@
//@hide: LOWPAN_SERVICE,
//@hide: WIFI_RTT_SERVICE,
//@hide: ETHERNET_SERVICE,
- WIFI_RTT_SERVICE,
+ WIFI_RTT_RANGING_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
FINGERPRINT_SERVICE,
@@ -3496,7 +3496,7 @@
* @see android.net.wifi.rtt.WifiRttManager
* @hide
*/
- public static final String WIFI_RTT2_SERVICE = "rttmanager2";
+ public static final String WIFI_RTT_RANGING_SERVICE = "rttmanager2";
/**
* Use with {@link #getSystemService} to retrieve a {@link
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 551d53b..21bd7f0 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -17,6 +17,7 @@
package android.content.pm;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -150,6 +151,7 @@
*
* @hide
*/
+ @TestApi
public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
/**
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 7049628..b111ad3 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -17,6 +17,7 @@
package android.hardware;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -70,7 +71,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
+ @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA})
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 223ed73..02b1c65 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -392,7 +392,7 @@
mWindow.setToken(token);
}
}
-
+
/**
* {@inheritDoc}
*
@@ -1064,7 +1064,89 @@
}
return mInputConnection;
}
-
+
+ /**
+ * Force switch to a new input method component. This can only be called
+ * from an application or a service which has a token of the currently active input method.
+ * @param id The unique identifier for the new input method to be switched to.
+ */
+ public void setInputMethod(String id) {
+ mImm.setInputMethodInternal(mToken, id);
+ }
+
+ /**
+ * Force switch to a new input method and subtype. This can only be called
+ * from an application or a service which has a token of the currently active input method.
+ * @param id The unique identifier for the new input method to be switched to.
+ * @param subtype The new subtype of the new input method to be switched to.
+ */
+ public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
+ mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype);
+ }
+
+ /**
+ * Close/hide the input method's soft input area, so the user no longer
+ * sees it or can interact with it. This can only be called
+ * from the currently active input method, as validated by the given token.
+ *
+ * @param flags Provides additional operating flags. Currently may be
+ * 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY},
+ * {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
+ */
+ public void hideSoftInputFromInputMethod(int flags) {
+ mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
+ }
+
+ /**
+ * Show the input method's soft input area, so the user
+ * sees the input method window and can interact with it.
+ * This can only be called from the currently active input method,
+ * as validated by the given token.
+ *
+ * @param flags Provides additional operating flags. Currently may be
+ * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} or
+ * {@link InputMethodManager#SHOW_FORCED} bit set.
+ */
+ public void showSoftInputFromInputMethod(int flags) {
+ mImm.showSoftInputFromInputMethodInternal(mToken, flags);
+ }
+
+ /**
+ * Force switch to the last used input method and subtype. If the last input method didn't have
+ * any subtypes, the framework will simply switch to the last input method with no subtype
+ * specified.
+ * @return true if the current input method and subtype was successfully switched to the last
+ * used input method and subtype.
+ */
+ public boolean switchToLastInputMethod() {
+ return mImm.switchToLastInputMethodInternal(mToken);
+ }
+
+ /**
+ * Force switch to the next input method and subtype. If there is no IME enabled except
+ * current IME and subtype, do nothing.
+ * @param onlyCurrentIme if true, the framework will find the next subtype which
+ * belongs to the current IME
+ * @return true if the current input method and subtype was successfully switched to the next
+ * input method and subtype.
+ */
+ public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
+ return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme);
+ }
+
+ /**
+ * Returns true if the current IME needs to offer the users ways to switch to a next input
+ * method (e.g. a globe key.).
+ * When an IME sets supportsSwitchingToNextInputMethod and this method returns true,
+ * the IME has to offer ways to to invoke {@link #switchToNextInputMethod} accordingly.
+ * <p> Note that the system determines the most appropriate next input method
+ * and subtype in order to provide the consistent user experience in switching
+ * between IMEs and subtypes.
+ */
+ public boolean shouldOfferSwitchingToNextInputMethod() {
+ return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken);
+ }
+
public boolean getCurrentInputStarted() {
return mInputStarted;
}
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index 8ff8e4f..6f383b4 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -74,7 +74,7 @@
j.add("final_score=" + finalScore);
}
j.add(String.format("duration=%.0fs", durationMs / 1000.0));
- j.add(String.format("validation=%4.1f%%", (validatedMs * 100.0) / durationMs));
+ j.add(String.format("validation=%04.1f%%", (validatedMs * 100.0) / durationMs));
return j.toString();
}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java b/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java
new file mode 100644
index 0000000..4125f0ba
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyGenerator.java
@@ -0,0 +1,133 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.util.Log;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableEntryException;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.security.auth.DestroyFailedException;
+
+/**
+ * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
+ *
+ * <p>Generates 256-bit AES keys, which can be used for encrypt / decrypt with AES/GCM/NoPadding.
+ * They are synced to disk wrapped by a platform key. This allows them to be exported to a remote
+ * service.
+ *
+ * @hide
+ */
+public class RecoverableKeyGenerator {
+ private static final String TAG = "RecoverableKeyGenerator";
+ private static final String KEY_GENERATOR_ALGORITHM = "AES";
+ private static final int KEY_SIZE_BITS = 256;
+
+ /**
+ * A new {@link RecoverableKeyGenerator} instance.
+ *
+ * @param platformKey Secret key used to wrap generated keys before persisting to disk.
+ * @param recoverableKeyStorage Class that manages persisting wrapped keys to disk.
+ * @throws NoSuchAlgorithmException if "AES" key generation or "AES/GCM/NoPadding" cipher is
+ * unavailable. Should never happen.
+ *
+ * @hide
+ */
+ public static RecoverableKeyGenerator newInstance(
+ AndroidKeyStoreSecretKey platformKey, RecoverableKeyStorage recoverableKeyStorage)
+ throws NoSuchAlgorithmException {
+ // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
+ // material, so that it can be synced to disk in encrypted form.
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
+ return new RecoverableKeyGenerator(keyGenerator, platformKey, recoverableKeyStorage);
+ }
+
+ private final KeyGenerator mKeyGenerator;
+ private final RecoverableKeyStorage mRecoverableKeyStorage;
+ private final AndroidKeyStoreSecretKey mPlatformKey;
+
+ private RecoverableKeyGenerator(
+ KeyGenerator keyGenerator,
+ AndroidKeyStoreSecretKey platformKey,
+ RecoverableKeyStorage recoverableKeyStorage) {
+ mKeyGenerator = keyGenerator;
+ mRecoverableKeyStorage = recoverableKeyStorage;
+ mPlatformKey = platformKey;
+ }
+
+ /**
+ * Generates a 256-bit AES key with the given alias.
+ *
+ * <p>Stores in the AndroidKeyStore, as well as persisting in wrapped form to disk. It is
+ * persisted to disk so that it can be synced remotely, and then recovered on another device.
+ * The generated key allows encrypt/decrypt only using AES/GCM/NoPadding.
+ *
+ * <p>The key handle returned to the caller is a reference to the AndroidKeyStore key,
+ * meaning that the caller is never able to access the raw, unencrypted key.
+ *
+ * @param alias The alias by which the key will be known in AndroidKeyStore.
+ * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
+ * @throws IOException if there was an issue writing the wrapped key to the wrapped key store.
+ * @throws UnrecoverableEntryException if could not retrieve key after putting it in
+ * AndroidKeyStore. This should not happen.
+ * @return A handle to the AndroidKeyStore key.
+ *
+ * @hide
+ */
+ public SecretKey generateAndStoreKey(String alias) throws KeyStoreException,
+ InvalidKeyException, IOException, UnrecoverableEntryException {
+ mKeyGenerator.init(KEY_SIZE_BITS);
+ SecretKey key = mKeyGenerator.generateKey();
+
+ mRecoverableKeyStorage.importIntoAndroidKeyStore(
+ alias,
+ key,
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(mPlatformKey, key);
+
+ try {
+ // Keep raw key material in memory for minimum possible time.
+ key.destroy();
+ } catch (DestroyFailedException e) {
+ Log.w(TAG, "Could not destroy SecretKey.");
+ }
+
+ mRecoverableKeyStorage.persistToDisk(alias, wrappedKey);
+
+ try {
+ // Reload from the keystore, so that the caller is only provided with the handle of the
+ // key, not the raw key material.
+ return mRecoverableKeyStorage.loadFromAndroidKeyStore(alias);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(
+ "Impossible: NoSuchAlgorithmException when attempting to retrieve a key "
+ + "that has only just been stored in AndroidKeyStore.", e);
+ }
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java
new file mode 100644
index 0000000..c239e00
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStorage.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import android.security.keystore.KeyProtection;
+
+import java.io.IOException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableEntryException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Stores wrapped keys to disk, so they can be synced on the next screen unlock event.
+ *
+ * @hide
+ */
+public interface RecoverableKeyStorage {
+
+ /**
+ * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}.
+ *
+ * @throws IOException if an error occurred writing to disk.
+ *
+ * @hide
+ */
+ void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException;
+
+ /**
+ * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and
+ * the {@code alias}.
+ *
+ * @param alias The alias of the key.
+ * @param key The key.
+ * @param keyProtection Protection params denoting what the key can be used for. (e.g., what
+ * Cipher modes, whether for encrpyt/decrypt or signing, etc.)
+ * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore.
+ *
+ * @hide
+ */
+ void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection) throws
+ KeyStoreException;
+
+ /**
+ * Loads a key handle from AndroidKeyStore.
+ *
+ * @param alias Alias of the key to load.
+ * @return The key handle.
+ * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore.
+ *
+ * @hide
+ */
+ SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException,
+ NoSuchAlgorithmException,
+ UnrecoverableEntryException;
+}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java
new file mode 100644
index 0000000..b9926dd
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStorageImpl.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import android.security.keystore.KeyProtection;
+
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Implementation of {@link RecoverableKeyStorage}.
+ *
+ * <p>Persists wrapped keys to disk, and loads raw keys into AndroidKeyStore.
+ *
+ * @hide
+ */
+public class RecoverableKeyStorageImpl implements RecoverableKeyStorage {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+
+ private final KeyStore mKeyStore;
+
+ /**
+ * A new instance.
+ *
+ * @throws KeyStoreException if unable to load AndroidKeyStore.
+ *
+ * @hide
+ */
+ public static RecoverableKeyStorageImpl newInstance() throws KeyStoreException {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ try {
+ keyStore.load(/*param=*/ null);
+ } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
+ // Should never happen.
+ throw new KeyStoreException("Unable to load keystore.", e);
+ }
+ return new RecoverableKeyStorageImpl(keyStore);
+ }
+
+ private RecoverableKeyStorageImpl(KeyStore keyStore) {
+ mKeyStore = keyStore;
+ }
+
+ /**
+ * Writes {@code wrappedKey} to disk, keyed by the application's uid and the {@code alias}.
+ *
+ * @throws IOException if an error occurred writing to disk.
+ *
+ * @hide
+ */
+ @Override
+ public void persistToDisk(String alias, WrappedKey wrappedKey) throws IOException {
+ // TODO(robertberry) Add implementation.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and the
+ * {@code alias}.
+ *
+ * @param alias The alias of the key.
+ * @param key The key.
+ * @param keyProtection Protection params denoting what the key can be used for. (e.g., what
+ * Cipher modes, whether for encrpyt/decrypt or signing, etc.)
+ * @throws KeyStoreException if an error occurred loading the key into the AndroidKeyStore.
+ *
+ * @hide
+ */
+ @Override
+ public void importIntoAndroidKeyStore(String alias, SecretKey key, KeyProtection keyProtection)
+ throws KeyStoreException {
+ mKeyStore.setEntry(alias, new KeyStore.SecretKeyEntry(key), keyProtection);
+ }
+
+ /**
+ * Loads a key handle from AndroidKeyStore.
+ *
+ * @param alias Alias of the key to load.
+ * @return The key handle.
+ * @throws KeyStoreException if an error occurred loading the key from AndroidKeyStore.
+ *
+ * @hide
+ */
+ @Override
+ public SecretKey loadFromAndroidKeyStore(String alias)
+ throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
+ return ((KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, /*protParam=*/ null))
+ .getSecretKey();
+ }
+}
diff --git a/core/java/android/security/recoverablekeystore/WrappedKey.java b/core/java/android/security/recoverablekeystore/WrappedKey.java
new file mode 100644
index 0000000..51665ae
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/WrappedKey.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding.
+ *
+ * @hide
+ */
+public class WrappedKey {
+ private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+
+ private final byte[] mNonce;
+ private final byte[] mKeyMaterial;
+
+ /**
+ * Returns a wrapped form of {@code key}, using {@code wrappingKey} to encrypt the key material.
+ *
+ * @throws InvalidKeyException if {@code wrappingKey} cannot be used to encrypt {@code key}, or
+ * if {@code key} does not expose its key material. See
+ * {@link android.security.keystore.AndroidKeyStoreKey} for an example of a key that does
+ * not expose its key material.
+ */
+ public static WrappedKey fromSecretKey(
+ SecretKey wrappingKey, SecretKey key) throws InvalidKeyException, KeyStoreException {
+ if (key.getEncoded() == null) {
+ throw new InvalidKeyException(
+ "key does not expose encoded material. It cannot be wrapped.");
+ }
+
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new RuntimeException(
+ "Android does not support AES/GCM/NoPadding. This should never happen.");
+ }
+
+ cipher.init(Cipher.WRAP_MODE, wrappingKey);
+ byte[] encryptedKeyMaterial;
+ try {
+ encryptedKeyMaterial = cipher.wrap(key);
+ } catch (IllegalBlockSizeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof KeyStoreException) {
+ // If AndroidKeyStore encounters any error here, it throws IllegalBlockSizeException
+ // with KeyStoreException as the cause. This is due to there being no better option
+ // here, as the Cipher#wrap only checked throws InvalidKeyException or
+ // IllegalBlockSizeException. If this is the case, we want to propagate it to the
+ // caller, so rethrow the cause.
+ throw (KeyStoreException) cause;
+ } else {
+ throw new RuntimeException(
+ "IllegalBlockSizeException should not be thrown by AES/GCM/NoPadding mode.",
+ e);
+ }
+ }
+
+ return new WrappedKey(/*mNonce=*/ cipher.getIV(), /*mKeyMaterial=*/ encryptedKeyMaterial);
+ }
+
+ /**
+ * A new instance.
+ *
+ * @param nonce The nonce with which the key material was encrypted.
+ * @param keyMaterial The encrypted bytes of the key material.
+ *
+ * @hide
+ */
+ public WrappedKey(byte[] nonce, byte[] keyMaterial) {
+ mNonce = nonce;
+ mKeyMaterial = keyMaterial;
+ }
+
+ /**
+ * Returns the nonce with which the key material was encrypted.
+ *
+ * @hide
+ */
+ public byte[] getNonce() {
+ return mNonce;
+ }
+
+ /**
+ * Returns the encrypted key material.
+ *
+ * @hide
+ */
+ public byte[] getKeyMaterial() {
+ return mKeyMaterial;
+ }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 80785fd..54b48b6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -43,6 +43,7 @@
DEFAULT_FLAGS.put("settings_app_info_v2", "false");
DEFAULT_FLAGS.put("settings_connected_device_v2", "false");
DEFAULT_FLAGS.put("settings_battery_v2", "false");
+ DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
}
/**
diff --git a/core/java/android/view/FrameInfo.java b/core/java/android/view/FrameInfo.java
index c79547c..6c5e048 100644
--- a/core/java/android/view/FrameInfo.java
+++ b/core/java/android/view/FrameInfo.java
@@ -16,7 +16,7 @@
package android.view;
-import android.annotation.IntDef;
+import android.annotation.LongDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -48,7 +48,7 @@
// Is this the first-draw following a window layout?
public static final long FLAG_WINDOW_LAYOUT_CHANGED = 1;
- @IntDef(flag = true, value = {
+ @LongDef(flag = true, value = {
FLAG_WINDOW_LAYOUT_CHANGED })
@Retention(RetentionPolicy.SOURCE)
public @interface FrameInfoFlags {}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 420a1bb..8a7b441 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -18,11 +18,18 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import static android.graphics.Matrix.MSCALE_X;
+import static android.graphics.Matrix.MSCALE_Y;
+import static android.graphics.Matrix.MSKEW_X;
+import static android.graphics.Matrix.MSKEW_Y;
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
import android.annotation.Size;
import android.graphics.Bitmap;
import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
@@ -862,6 +869,22 @@
}
}
+ /**
+ * Sets the transform and position of a {@link SurfaceControl} from a 3x3 transformation matrix.
+ *
+ * @param matrix The matrix to apply.
+ * @param float9 An array of 9 floats to be used to extract the values from the matrix.
+ */
+ public void setMatrix(Matrix matrix, float[] float9) {
+ checkNotReleased();
+ matrix.getValues(float9);
+ synchronized (SurfaceControl.class) {
+ sGlobalTransaction.setMatrix(this, float9[MSCALE_X], float9[MSKEW_Y],
+ float9[MSKEW_X], float9[MSCALE_Y]);
+ sGlobalTransaction.setPosition(this, float9[MTRANS_X], float9[MTRANS_Y]);
+ }
+ }
+
public void setWindowCrop(Rect crop) {
checkNotReleased();
synchronized (SurfaceControl.class) {
@@ -1348,6 +1371,14 @@
return this;
}
+ public Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) {
+ matrix.getValues(float9);
+ setMatrix(sc, float9[MSCALE_X], float9[MSKEW_Y],
+ float9[MSKEW_X], float9[MSCALE_Y]);
+ setPosition(sc, float9[MTRANS_X], float9[MTRANS_Y]);
+ return this;
+ }
+
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
sc.checkNotReleased();
if (crop != null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0bae36b..6d4998b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7907,6 +7907,7 @@
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+ mContext.getPackageName(),
new AccessibilityInteractionConnection(ViewRootImpl.this));
}
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7c2c12f..500701d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -20,6 +20,7 @@
import android.Manifest.permission;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -1270,7 +1271,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(
+ @LongDef(
flag = true,
value = {
LayoutParams.FLAG2_LAYOUT_IN_DISPLAY_CUTOUT_AREA,
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index d890f32..e146555 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -29,6 +29,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -213,7 +214,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @return The {@link AccessibilityWindowInfo}.
*/
@@ -299,7 +300,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -335,18 +336,19 @@
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success;
+ final String[] packageNames;
try {
- success = connection.findAccessibilityNodeInfoByAccessibilityId(
+ packageNames = connection.findAccessibilityNodeInfoByAccessibilityId(
accessibilityWindowId, accessibilityNodeId, interactionId, this,
prefetchFlags, Thread.currentThread().getId(), arguments);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- if (success) {
+ if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
+ bypassCache, packageNames);
if (infos != null && !infos.isEmpty()) {
for (int i = 1; i < infos.size(); i++) {
infos.get(i).recycle();
@@ -373,7 +375,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -389,20 +391,21 @@
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success;
+ final String[] packageNames;
try {
- success = connection.findAccessibilityNodeInfosByViewId(
+ packageNames = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- if (success) {
+ if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
+ false, packageNames);
return infos;
}
}
@@ -426,7 +429,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -442,20 +445,21 @@
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success;
+ final String[] packageNames;
try {
- success = connection.findAccessibilityNodeInfosByText(
+ packageNames = connection.findAccessibilityNodeInfosByText(
accessibilityWindowId, accessibilityNodeId, text, interactionId, this,
Thread.currentThread().getId());
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- if (success) {
+ if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
if (infos != null) {
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false);
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
+ false, packageNames);
return infos;
}
}
@@ -478,7 +482,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -494,19 +498,19 @@
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success;
+ final String[] packageNames;
try {
- success = connection.findFocus(accessibilityWindowId,
+ packageNames = connection.findFocus(accessibilityWindowId,
accessibilityNodeId, focusType, interactionId, this,
Thread.currentThread().getId());
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- if (success) {
+ if (packageNames != null) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
return info;
}
} else {
@@ -527,7 +531,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -543,19 +547,19 @@
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
final long identityToken = Binder.clearCallingIdentity();
- final boolean success;
+ final String[] packageNames;
try {
- success = connection.focusSearch(accessibilityWindowId,
+ packageNames = connection.focusSearch(accessibilityWindowId,
accessibilityNodeId, direction, interactionId, this,
Thread.currentThread().getId());
} finally {
Binder.restoreCallingIdentity(identityToken);
}
- if (success) {
+ if (packageNames != null) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
return info;
}
} else {
@@ -574,7 +578,7 @@
*
* @param connectionId The id of a connection for interacting with the system.
* @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+ * {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
* to query the currently active window.
* @param accessibilityNodeId A unique view id or virtual descendant id from
* where to start the search. Use
@@ -661,7 +665,7 @@
int interactionId) {
synchronized (mInstanceLock) {
final boolean success = waitForResultTimedLocked(interactionId);
- List<AccessibilityNodeInfo> result = null;
+ final List<AccessibilityNodeInfo> result;
if (success) {
result = mFindAccessibilityNodeInfosResult;
} else {
@@ -779,11 +783,19 @@
* @param connectionId The id of the connection to the system.
* @param bypassCache Whether or not to bypass the cache. The node is added to the cache if
* this value is {@code false}
+ * @param packageNames The valid package names a node can come from.
*/
private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info,
- int connectionId, boolean bypassCache) {
+ int connectionId, boolean bypassCache, String[] packageNames) {
if (info != null) {
info.setConnectionId(connectionId);
+ // Empty array means any package name is Okay
+ if (!ArrayUtils.isEmpty(packageNames)
+ && !ArrayUtils.contains(packageNames, info.getPackageName().toString())) {
+ // If the node package not one of the valid ones, pick the top one - this
+ // is one of the packages running in the introspected UID.
+ info.setPackageName(packageNames[0]);
+ }
info.setSealed(true);
if (!bypassCache) {
sAccessibilityCache.add(info);
@@ -798,14 +810,16 @@
* @param connectionId The id of the connection to the system.
* @param bypassCache Whether or not to bypass the cache. The nodes are added to the cache if
* this value is {@code false}
+ * @param packageNames The valid package names a node can come from.
*/
private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
- int connectionId, boolean bypassCache) {
+ int connectionId, boolean bypassCache, String[] packageNames) {
if (infos != null) {
final int infosCount = infos.size();
for (int i = 0; i < infosCount; i++) {
AccessibilityNodeInfo info = infos.get(i);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId, bypassCache);
+ finalizeAndCacheAccessibilityNodeInfo(info, connectionId,
+ bypassCache, packageNames);
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 35f6acb..b4499d1 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -888,7 +888,7 @@
* @hide
*/
public int addAccessibilityInteractionConnection(IWindow windowToken,
- IAccessibilityInteractionConnection connection) {
+ String packageName, IAccessibilityInteractionConnection connection) {
final IAccessibilityManager service;
final int userId;
synchronized (mLock) {
@@ -899,7 +899,8 @@
userId = mUserId;
}
try {
- return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
+ return service.addAccessibilityInteractionConnection(windowToken, connection,
+ packageName, userId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index faea920..9c2f6bb 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3695,8 +3695,9 @@
if (DEBUG) {
builder.append("; sourceNodeId: " + mSourceNodeId);
- builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
- builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
+ builder.append("; windowId: " + mWindowId);
+ builder.append("; accessibilityViewId: ").append(getAccessibilityViewId(mSourceNodeId));
+ builder.append("; virtualDescendantId: ").append(getVirtualDescendantId(mSourceNodeId));
builder.append("; mParentNodeId: " + mParentNodeId);
builder.append("; traversalBefore: ").append(mTraversalBefore);
builder.append("; traversalAfter: ").append(mTraversalAfter);
@@ -3726,8 +3727,8 @@
builder.append("]");
}
- builder.append("; boundsInParent: " + mBoundsInParent);
- builder.append("; boundsInScreen: " + mBoundsInScreen);
+ builder.append("; boundsInParent: ").append(mBoundsInParent);
+ builder.append("; boundsInScreen: ").append(mBoundsInScreen);
builder.append("; packageName: ").append(mPackageName);
builder.append("; className: ").append(mClassName);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 3f499ab..c93e2c1 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -45,7 +45,8 @@
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
int addAccessibilityInteractionConnection(IWindow windowToken,
- in IAccessibilityInteractionConnection connection, int userId);
+ in IAccessibilityInteractionConnection connection,
+ String packageName, int userId);
void removeAccessibilityInteractionConnection(IWindow windowToken);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4d96733..3cd8d4a 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -24,6 +24,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -735,7 +736,20 @@
}
}
+ /**
+ * @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in this
+ * class are intended for app developers interacting with the IME.
+ */
+ @Deprecated
public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
+ showStatusIconInternal(imeToken, packageName, iconId);
+ }
+
+ /**
+ * @hide
+ */
+ public void showStatusIconInternal(IBinder imeToken, String packageName, int iconId) {
try {
mService.updateStatusIcon(imeToken, packageName, iconId);
} catch (RemoteException e) {
@@ -743,7 +757,20 @@
}
}
+ /**
+ * @deprecated Use {@link InputMethodService#hideStatusIcon()} instead. This method was
+ * intended for IME developers who should be accessing APIs through the service. APIs in
+ * this class are intended for app developers interacting with the IME.
+ */
+ @Deprecated
public void hideStatusIcon(IBinder imeToken) {
+ hideStatusIconInternal(imeToken);
+ }
+
+ /**
+ * @hide
+ */
+ public void hideStatusIconInternal(IBinder imeToken) {
try {
mService.updateStatusIcon(imeToken, null, 0);
} catch (RemoteException e) {
@@ -1121,7 +1148,6 @@
}
}
-
/**
* This method toggles the input method window display.
* If the input window is already displayed, it gets hidden.
@@ -1800,8 +1826,19 @@
* when it was started, which allows it to perform this operation on
* itself.
* @param id The unique identifier for the new input method to be switched to.
+ * @deprecated Use {@link InputMethodService#setInputMethod(String)} instead. This method
+ * was intended for IME developers who should be accessing APIs through the service. APIs in
+ * this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public void setInputMethod(IBinder token, String id) {
+ setInputMethodInternal(token, id);
+ }
+
+ /**
+ * @hide
+ */
+ public void setInputMethodInternal(IBinder token, String id) {
try {
mService.setInputMethod(token, id);
} catch (RemoteException e) {
@@ -1817,8 +1854,21 @@
* itself.
* @param id The unique identifier for the new input method to be switched to.
* @param subtype The new subtype of the new input method to be switched to.
+ * @deprecated Use
+ * {@link InputMethodService#setInputMethodAndSubtype(String, InputMethodSubtype)}
+ * instead. This method was intended for IME developers who should be accessing APIs through
+ * the service. APIs in this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+ setInputMethodAndSubtypeInternal(token, id, subtype);
+ }
+
+ /**
+ * @hide
+ */
+ public void setInputMethodAndSubtypeInternal(
+ IBinder token, String id, InputMethodSubtype subtype) {
try {
mService.setInputMethodAndSubtype(token, id, subtype);
} catch (RemoteException e) {
@@ -1837,8 +1887,19 @@
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #HIDE_IMPLICIT_ONLY},
* {@link #HIDE_NOT_ALWAYS} bit set.
+ * @deprecated Use {@link InputMethodService#hideSoftInputFromInputMethod(int)}
+ * instead. This method was intended for IME developers who should be accessing APIs through
+ * the service. APIs in this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
+ hideSoftInputFromInputMethodInternal(token, flags);
+ }
+
+ /**
+ * @hide
+ */
+ public void hideSoftInputFromInputMethodInternal(IBinder token, int flags) {
try {
mService.hideMySoftInput(token, flags);
} catch (RemoteException e) {
@@ -1858,8 +1919,19 @@
* @param flags Provides additional operating flags. Currently may be
* 0 or have the {@link #SHOW_IMPLICIT} or
* {@link #SHOW_FORCED} bit set.
+ * @deprecated Use {@link InputMethodService#showSoftInputFromInputMethod(int)}
+ * instead. This method was intended for IME developers who should be accessing APIs through
+ * the service. APIs in this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
+ showSoftInputFromInputMethodInternal(token, flags);
+ }
+
+ /**
+ * @hide
+ */
+ public void showSoftInputFromInputMethodInternal(IBinder token, int flags) {
try {
mService.showMySoftInput(token, flags);
} catch (RemoteException e) {
@@ -2239,8 +2311,19 @@
* which allows it to perform this operation on itself.
* @return true if the current input method and subtype was successfully switched to the last
* used input method and subtype.
+ * @deprecated Use {@link InputMethodService#switchToLastInputMethod()} instead. This method
+ * was intended for IME developers who should be accessing APIs through the service. APIs in
+ * this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public boolean switchToLastInputMethod(IBinder imeToken) {
+ return switchToLastInputMethodInternal(imeToken);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean switchToLastInputMethodInternal(IBinder imeToken) {
synchronized (mH) {
try {
return mService.switchToLastInputMethod(imeToken);
@@ -2259,8 +2342,19 @@
* belongs to the current IME
* @return true if the current input method and subtype was successfully switched to the next
* input method and subtype.
+ * @deprecated Use {@link InputMethodService#switchToNextInputMethod(boolean)} instead. This
+ * method was intended for IME developers who should be accessing APIs through the service.
+ * APIs in this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
+ return switchToNextInputMethodInternal(imeToken, onlyCurrentIme);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean switchToNextInputMethodInternal(IBinder imeToken, boolean onlyCurrentIme) {
synchronized (mH) {
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
@@ -2280,8 +2374,19 @@
* between IMEs and subtypes.
* @param imeToken Supplies the identifying token given to an input method when it was started,
* which allows it to perform this operation on itself.
+ * @deprecated Use {@link InputMethodService#shouldOfferSwitchingToNextInputMethod()}
+ * instead. This method was intended for IME developers who should be accessing APIs through
+ * the service. APIs in this class are intended for app developers interacting with the IME.
*/
+ @Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
+ return shouldOfferSwitchingToNextInputMethodInternal(imeToken);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean shouldOfferSwitchingToNextInputMethodInternal(IBinder imeToken) {
synchronized (mH) {
try {
return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index f4cbc54..fdc9f92 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -48,8 +48,13 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
- TYPE_UNKNOWN, TYPE_OTHER, TYPE_EMAIL, TYPE_PHONE, TYPE_ADDRESS, TYPE_URL
+ @StringDef(prefix = { "TYPE_" }, value = {
+ TYPE_UNKNOWN,
+ TYPE_OTHER,
+ TYPE_EMAIL,
+ TYPE_PHONE,
+ TYPE_ADDRESS,
+ TYPE_URL,
})
@interface EntityType {}
diff --git a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
index 931eb99..45555bf 100644
--- a/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
+++ b/core/java/com/android/internal/graphics/SfVsyncFrameCallbackProvider.java
@@ -26,7 +26,15 @@
*/
public final class SfVsyncFrameCallbackProvider implements AnimationFrameCallbackProvider {
- private final Choreographer mChoreographer = Choreographer.getSfInstance();
+ private final Choreographer mChoreographer;
+
+ public SfVsyncFrameCallbackProvider() {
+ mChoreographer = Choreographer.getSfInstance();
+ }
+
+ public SfVsyncFrameCallbackProvider(Choreographer choreographer) {
+ mChoreographer = choreographer;
+ }
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h
index 19628e5..3a05195 100644
--- a/core/jni/eventlog_helper.h
+++ b/core/jni/eventlog_helper.h
@@ -155,6 +155,11 @@
return;
}
+ ScopedIntArrayRO tags(env);
+ if (jTags != nullptr) {
+ tags.reset(jTags);
+ }
+
while (1) {
log_msg log_msg;
int ret = android_logger_list_read(logger_list.get(), &log_msg);
@@ -182,7 +187,6 @@
if (jTags != nullptr) {
bool found = false;
- ScopedIntArrayRO tags(env, jTags);
for (size_t i = 0; !found && i < tags.size(); ++i) {
found = (tag == tags[i]);
}
diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java
new file mode 100644
index 0000000..d85d3b8
--- /dev/null
+++ b/core/tests/coretests/src/android/security/recoverablekeystore/RecoverableKeyGeneratorTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.security.KeyStore;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyGeneratorTest {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String TEST_ALIAS = "karlin";
+ private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyGeneratorTestWrappingKey";
+
+ @Mock RecoverableKeyStorage mRecoverableKeyStorage;
+
+ @Captor ArgumentCaptor<KeyProtection> mKeyProtectionArgumentCaptor;
+
+ private AndroidKeyStoreSecretKey mPlatformKey;
+ private SecretKey mKeyHandle;
+ private RecoverableKeyGenerator mRecoverableKeyGenerator;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPlatformKey = generateAndroidKeyStoreKey();
+ mKeyHandle = generateKey();
+ mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(
+ mPlatformKey, mRecoverableKeyStorage);
+
+ when(mRecoverableKeyStorage.loadFromAndroidKeyStore(any())).thenReturn(mKeyHandle);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ keyStore.load(/*param=*/ null);
+ keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+ }
+
+ @Test
+ public void generateAndStoreKey_setsKeyInKeyStore() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ verify(mRecoverableKeyStorage, times(1))
+ .importIntoAndroidKeyStore(eq(TEST_ALIAS), any(), any());
+ }
+
+ @Test
+ public void generateAndStoreKey_storesKeyEnabledForEncryptDecrypt() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ KeyProtection keyProtection = getKeyProtectionUsed();
+ assertEquals(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+ keyProtection.getPurposes());
+ }
+
+ @Test
+ public void generateAndStoreKey_storesKeyEnabledForGCM() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ KeyProtection keyProtection = getKeyProtectionUsed();
+ assertArrayEquals(new String[] { KeyProperties.BLOCK_MODE_GCM },
+ keyProtection.getBlockModes());
+ }
+
+ @Test
+ public void generateAndStoreKey_storesKeyEnabledForNoPadding() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ KeyProtection keyProtection = getKeyProtectionUsed();
+ assertArrayEquals(new String[] { KeyProperties.ENCRYPTION_PADDING_NONE },
+ keyProtection.getEncryptionPaddings());
+ }
+
+ @Test
+ public void generateAndStoreKey_storesWrappedKey() throws Exception {
+ mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ verify(mRecoverableKeyStorage, times(1)).persistToDisk(eq(TEST_ALIAS), any());
+ }
+
+ @Test
+ public void generateAndStoreKey_returnsKeyHandle() throws Exception {
+ SecretKey secretKey = mRecoverableKeyGenerator.generateAndStoreKey(TEST_ALIAS);
+
+ assertEquals(mKeyHandle, secretKey);
+ }
+
+ private KeyProtection getKeyProtectionUsed() throws Exception {
+ verify(mRecoverableKeyStorage, times(1)).importIntoAndroidKeyStore(
+ any(), any(), mKeyProtectionArgumentCaptor.capture());
+ return mKeyProtectionArgumentCaptor.getValue();
+ }
+
+ private SecretKey generateKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
+ keyGenerator.init(/*keySize=*/ 256);
+ return keyGenerator.generateKey();
+ }
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(
+ WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
+}
diff --git a/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java b/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java
new file mode 100644
index 0000000..233c821
--- /dev/null
+++ b/core/tests/coretests/src/android/security/recoverablekeystore/WrappedKeyTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 android.security.recoverablekeystore;
+
+import static org.junit.Assert.assertEquals;
+
+import android.security.keystore.AndroidKeyStoreSecretKey;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.KeyStore;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WrappedKeyTest {
+ private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+ private static final String KEY_ALGORITHM = "AES";
+ private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+ private static final String WRAPPING_KEY_ALIAS = "WrappedKeyTestWrappingKeyAlias";
+ private static final int GCM_TAG_LENGTH_BYTES = 16;
+ private static final int BITS_PER_BYTE = 8;
+ private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
+ keyStore.load(/*param=*/ null);
+ keyStore.deleteEntry(WRAPPING_KEY_ALIAS);
+ }
+
+ @Test
+ public void fromSecretKey_createsWrappedKeyThatCanBeUnwrapped() throws Exception {
+ SecretKey wrappingKey = generateAndroidKeyStoreKey();
+ SecretKey rawKey = generateKey();
+
+ WrappedKey wrappedKey = WrappedKey.fromSecretKey(wrappingKey, rawKey);
+
+ Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+ cipher.init(
+ Cipher.UNWRAP_MODE,
+ wrappingKey,
+ new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+ SecretKey unwrappedKey = (SecretKey) cipher.unwrap(
+ wrappedKey.getKeyMaterial(), KEY_ALGORITHM, Cipher.SECRET_KEY);
+ assertEquals(rawKey, unwrappedKey);
+ }
+
+ private SecretKey generateKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+ keyGenerator.init(/*keySize=*/ 256);
+ return keyGenerator.generateKey();
+ }
+
+ private AndroidKeyStoreSecretKey generateAndroidKeyStoreKey() throws Exception {
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KEY_ALGORITHM,
+ ANDROID_KEY_STORE_PROVIDER);
+ keyGenerator.init(new KeyGenParameterSpec.Builder(
+ WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ return (AndroidKeyStoreSecretKey) keyGenerator.generateKey();
+ }
+}
diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java
index b5140e3..e9eb978 100644
--- a/core/tests/coretests/src/android/util/ScrollViewScenario.java
+++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java
@@ -269,6 +269,6 @@
mScrollView.setSmoothScrollingEnabled(false);
setContentView(mScrollView);
- mScrollView.restoreDefaultFocus();
+ mScrollView.post(() -> mScrollView.restoreDefaultFocus());
}
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
index 8a5fc2d..c1b2309 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityInteractionClientTest.java
@@ -24,6 +24,7 @@
import android.os.RemoteException;
import android.support.test.runner.AndroidJUnit4;
+import libcore.util.EmptyArray;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,7 +74,7 @@
List<AccessibilityNodeInfo> mInfosToReturn;
@Override
- public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
Bundle arguments) {
@@ -82,7 +83,7 @@
} catch (RemoteException e) {
throw new RuntimeException(e);
}
- return true;
+ return EmptyArray.STRING;
}
}
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index d3bbee7..44b1f08 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -31,35 +31,35 @@
public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub {
public void setServiceInfo(AccessibilityServiceInfo info) {}
- public boolean findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
Bundle arguments) {
- return false;
+ return null;
}
- public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId,
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId) {
- return false;
+ return null;
}
- public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId) {
- return false;
+ return null;
}
- public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
+ public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId, int focusType,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId) {
- return false;
+ return null;
}
- public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
+ public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId, int direction,
int interactionId, IAccessibilityInteractionConnectionCallback callback,
long threadId) {
- return false;
+ return null;
}
public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index d6b1cf1..a250d1f0 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -39,13 +39,12 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- value = {
- PURPOSE_ENCRYPT,
- PURPOSE_DECRYPT,
- PURPOSE_SIGN,
- PURPOSE_VERIFY,
- })
+ @IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
+ PURPOSE_ENCRYPT,
+ PURPOSE_DECRYPT,
+ PURPOSE_SIGN,
+ PURPOSE_VERIFY,
+ })
public @interface PurposeEnum {}
/**
@@ -126,7 +125,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
+ @StringDef(prefix = { "KEY_" }, value = {
KEY_ALGORITHM_RSA,
KEY_ALGORITHM_EC,
KEY_ALGORITHM_AES,
@@ -267,7 +266,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
+ @StringDef(prefix = { "BLOCK_MODE_" }, value = {
BLOCK_MODE_ECB,
BLOCK_MODE_CBC,
BLOCK_MODE_CTR,
@@ -354,7 +353,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
+ @StringDef(prefix = { "ENCRYPTION_PADDING_" }, value = {
ENCRYPTION_PADDING_NONE,
ENCRYPTION_PADDING_PKCS7,
ENCRYPTION_PADDING_RSA_PKCS1,
@@ -437,7 +436,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
+ @StringDef(prefix = { "SIGNATURE_PADDING_" }, value = {
SIGNATURE_PADDING_RSA_PKCS1,
SIGNATURE_PADDING_RSA_PSS,
})
@@ -497,7 +496,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @StringDef({
+ @StringDef(prefix = { "DIGEST_" }, value = {
DIGEST_NONE,
DIGEST_MD5,
DIGEST_SHA1,
@@ -647,11 +646,12 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({
- ORIGIN_GENERATED,
- ORIGIN_IMPORTED,
- ORIGIN_UNKNOWN,
- })
+ @IntDef(prefix = { "ORIGIN_" }, value = {
+ ORIGIN_GENERATED,
+ ORIGIN_IMPORTED,
+ ORIGIN_UNKNOWN,
+ })
+
public @interface OriginEnum {}
/** Key was generated inside AndroidKeyStore. */
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index b15e0a2..7cb8e37 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -81,12 +81,16 @@
}
out.writeByteArray(mSpec.getCertificateSubject().getEncoded());
out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray());
- writeOptionalDate(out, mSpec.getCertificateNotBefore());
- writeOptionalDate(out, mSpec.getCertificateNotAfter());
+ out.writeLong(mSpec.getCertificateNotBefore().getTime());
+ out.writeLong(mSpec.getCertificateNotAfter().getTime());
writeOptionalDate(out, mSpec.getKeyValidityStart());
writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd());
writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd());
- out.writeStringArray(mSpec.getDigests());
+ if (mSpec.isDigestsSpecified()) {
+ out.writeStringArray(mSpec.getDigests());
+ } else {
+ out.writeStringArray(null);
+ }
out.writeStringArray(mSpec.getEncryptionPaddings());
out.writeStringArray(mSpec.getSignaturePaddings());
out.writeStringArray(mSpec.getBlockModes());
@@ -111,9 +115,15 @@
private ParcelableKeyGenParameterSpec(Parcel in) {
String keystoreAlias = in.readString();
int purposes = in.readInt();
- KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes);
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+ keystoreAlias, purposes);
builder.setUid(in.readInt());
- builder.setKeySize(in.readInt());
+ // KeySize is -1 by default, if the KeyGenParameterSpec previously parcelled had the default
+ // value, do not set it as this will cause setKeySize to throw.
+ int keySize = in.readInt();
+ if (keySize >= 0) {
+ builder.setKeySize(keySize);
+ }
int keySpecType = in.readInt();
AlgorithmParameterSpec algorithmSpec = null;
@@ -128,17 +138,22 @@
algorithmSpec = new ECGenParameterSpec(stdName);
} else {
throw new IllegalArgumentException(
- String.format("Unknown algorithm parameter spec: %d", algorithmSpec));
+ String.format("Unknown algorithm parameter spec: %d", keySpecType));
}
- builder.setAlgorithmParameterSpec(algorithmSpec);
+ if (algorithmSpec != null) {
+ builder.setAlgorithmParameterSpec(algorithmSpec);
+ }
builder.setCertificateSubject(new X500Principal(in.createByteArray()));
builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
- builder.setCertificateNotBefore(readDateOrNull(in));
- builder.setCertificateNotAfter(readDateOrNull(in));
+ builder.setCertificateNotBefore(new Date(in.readLong()));
+ builder.setCertificateNotAfter(new Date(in.readLong()));
builder.setKeyValidityStart(readDateOrNull(in));
builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
- builder.setDigests(in.createStringArray());
+ String[] digests = in.createStringArray();
+ if (digests != null) {
+ builder.setDigests(digests);
+ }
builder.setEncryptionPaddings(in.createStringArray());
builder.setSignaturePaddings(in.createStringArray());
builder.setBlockModes(in.createStringArray());
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 96e4fcf..7cacaf6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -122,7 +122,7 @@
instrumentation: true,
profile_file: "hwui/hwui.profdata",
benchmarks: ["hwui"],
- enable_profile_use: false,
+ enable_profile_use: true,
},
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 288d039..107890e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -45,8 +45,7 @@
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
- grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend,
- (GrBackendContext)glInterface.get()));
+ grContext = GrContext::MakeGL(std::move(glInterface));
} else {
grContext->resetContext();
}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 5e89fae..907f2d2 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -53,13 +53,13 @@
}
}
-void CacheManager::reset(GrContext* context) {
- if (context != mGrContext.get()) {
+void CacheManager::reset(sk_sp<GrContext> context) {
+ if (context != mGrContext) {
destroy();
}
if (context) {
- mGrContext = sk_ref_sp(context);
+ mGrContext = std::move(context);
mGrContext->getResourceCacheLimits(&mMaxResources, nullptr);
updateContextCacheSizes();
}
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index d037045..7d73352 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -61,7 +61,7 @@
CacheManager(const DisplayInfo& display);
- void reset(GrContext* grContext);
+ void reset(sk_sp<GrContext> grContext);
void destroy();
void updateContextCacheSizes();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 4df7caf..848c6a8 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -141,8 +141,9 @@
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
mRenderThread.cacheManager().configureContext(&options);
- mRenderThread.setGrContext(GrContext::Create(GrBackend::kOpenGL_GrBackend,
- (GrBackendContext)glInterface.get(), options));
+ sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
+ LOG_ALWAYS_FATAL_IF(!grContext.get());
+ mRenderThread.setGrContext(grContext);
}
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 20443ec..79dc09f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -173,12 +173,12 @@
return *mReadback;
}
-void RenderThread::setGrContext(GrContext* context) {
+void RenderThread::setGrContext(sk_sp<GrContext> context) {
mCacheManager->reset(context);
- if (mGrContext.get()) {
+ if (mGrContext) {
mGrContext->releaseResourcesAndAbandonContext();
}
- mGrContext.reset(context);
+ mGrContext = std::move(context);
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 970537b..3aa5487 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -88,7 +88,7 @@
const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; }
GrContext* getGrContext() const { return mGrContext.get(); }
- void setGrContext(GrContext* cxt);
+ void setGrContext(sk_sp<GrContext> cxt);
CacheManager& cacheManager() { return *mCacheManager; }
VulkanManager& vulkanManager() { return *mVkManager; }
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a693e68..9d246ff 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -58,6 +58,7 @@
mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr,
&mPresentQueueIndex, canPresent));
+ LOG_ALWAYS_FATAL_IF(!mBackendContext.get());
// Get all the addresses of needed vulkan functions
VkInstance instance = mBackendContext->fInstance;
@@ -110,8 +111,9 @@
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
mRenderThread.cacheManager().configureContext(&options);
- mRenderThread.setGrContext(
- GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options));
+ sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
+ LOG_ALWAYS_FATAL_IF(!grContext.get());
+ mRenderThread.setGrContext(grContext);
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 12e5744..e2f9b47 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -977,7 +977,7 @@
public static final String PROPERTY_ALGORITHMS = "algorithms";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "PROPERTY_" }, value = {
PROPERTY_VENDOR,
PROPERTY_VERSION,
PROPERTY_DESCRIPTION,
@@ -1010,7 +1010,7 @@
public static final String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "PROPERTY_" }, value = {
PROPERTY_DEVICE_UNIQUE_ID,
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 31eb948..94d4d55 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -45,34 +45,61 @@
/**
* @hide
*/
- @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
- METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
- METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
- METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
- METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
- METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI})
+ @StringDef(prefix = { "METADATA_KEY_" }, value = {
+ METADATA_KEY_TITLE,
+ METADATA_KEY_ARTIST,
+ METADATA_KEY_ALBUM,
+ METADATA_KEY_AUTHOR,
+ METADATA_KEY_WRITER,
+ METADATA_KEY_COMPOSER,
+ METADATA_KEY_COMPILATION,
+ METADATA_KEY_DATE,
+ METADATA_KEY_GENRE,
+ METADATA_KEY_ALBUM_ARTIST,
+ METADATA_KEY_ART_URI,
+ METADATA_KEY_ALBUM_ART_URI,
+ METADATA_KEY_DISPLAY_TITLE,
+ METADATA_KEY_DISPLAY_SUBTITLE,
+ METADATA_KEY_DISPLAY_DESCRIPTION,
+ METADATA_KEY_DISPLAY_ICON_URI,
+ METADATA_KEY_MEDIA_ID,
+ METADATA_KEY_MEDIA_URI,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface TextKey {}
/**
* @hide
*/
- @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
- METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE})
+ @StringDef(prefix = { "METADATA_KEY_" }, value = {
+ METADATA_KEY_DURATION,
+ METADATA_KEY_YEAR,
+ METADATA_KEY_TRACK_NUMBER,
+ METADATA_KEY_NUM_TRACKS,
+ METADATA_KEY_DISC_NUMBER,
+ METADATA_KEY_BT_FOLDER_TYPE,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface LongKey {}
/**
* @hide
*/
- @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
+ @StringDef(prefix = { "METADATA_KEY_" }, value = {
+ METADATA_KEY_ART,
+ METADATA_KEY_ALBUM_ART,
+ METADATA_KEY_DISPLAY_ICON,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface BitmapKey {}
/**
* @hide
*/
- @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
+ @StringDef(prefix = { "METADATA_KEY_" }, value = {
+ METADATA_KEY_USER_RATING,
+ METADATA_KEY_RATING,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface RatingKey {}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 8283c8b..17d16b8 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -17,6 +17,7 @@
import android.annotation.DrawableRes;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.Nullable;
import android.media.RemoteControlClient;
import android.os.Bundle;
@@ -41,7 +42,7 @@
/**
* @hide
*/
- @IntDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
+ @LongDef(flag=true, value={ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND,
ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 0f46096..3bbc2c4 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1650,7 +1650,7 @@
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "TYPE_" }, value = {
TYPE_OTHER,
TYPE_NTSC,
TYPE_PAL,
@@ -1863,7 +1863,7 @@
public static final String TYPE_PREVIEW = "TYPE_PREVIEW";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "SERVICE_TYPE_" }, value = {
SERVICE_TYPE_OTHER,
SERVICE_TYPE_AUDIO_VIDEO,
SERVICE_TYPE_AUDIO,
@@ -1881,7 +1881,7 @@
public static final String SERVICE_TYPE_AUDIO = "SERVICE_TYPE_AUDIO";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "VIDEO_FORMAT_" }, value = {
VIDEO_FORMAT_240P,
VIDEO_FORMAT_360P,
VIDEO_FORMAT_480I,
@@ -1930,7 +1930,7 @@
public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
/** @hide */
- @StringDef({
+ @StringDef(prefix = { "VIDEO_RESOLUTION_" }, value = {
VIDEO_RESOLUTION_SD,
VIDEO_RESOLUTION_ED,
VIDEO_RESOLUTION_HD,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 3b54e11..5c8c3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -333,7 +333,7 @@
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
- if (mDevicePolicyManager.isLogoutButtonEnabled()
+ if (mDevicePolicyManager.isLogoutEnabled()
&& getCurrentUser().id != UserHandle.USER_SYSTEM) {
mItems.add(new LogoutAction());
mHasLogoutButton = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 195607d..7374f11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -448,7 +448,7 @@
childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
childState.hidden = false;
ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
- if (!isTopEntry && (!mIsExpanded
+ if (topState != null && !isTopEntry && (!mIsExpanded
|| unmodifiedEndLocation < topState.yTranslation + topState.height)) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
// the top most z-position
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 7e94d7b..01679dd 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -20,9 +20,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -49,7 +47,6 @@
import android.view.View;
import android.view.accessibility.AccessibilityCache;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -58,6 +55,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
+import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection;
import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy;
import com.android.server.wm.WindowManagerInternal;
@@ -171,13 +169,13 @@
@NonNull MagnificationController getMagnificationController();
/**
- * Resolve a connection for a window id
+ * Resolve a connection wrapper for a window id
*
* @param windowId The id of the window of interest
*
* @return a connection to the window
*/
- IAccessibilityInteractionConnection getConnectionLocked(int windowId);
+ RemoteAccessibilityConnection getConnectionLocked(int windowId);
/**
* Perform the specified accessibility action
@@ -416,28 +414,28 @@
}
@Override
- public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
- return false;
+ return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
- return false;
+ return null;
} else {
connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
if (connection == null) {
- return false;
+ return null;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
@@ -450,12 +448,14 @@
final int interrogatingPid = Binder.getCallingPid();
callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
+ final int callingUid = Binder.getCallingUid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId, viewIdResName,
- partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
+ viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec);
- return true;
+ return mSecurityPolicy.computeValidReportedPackages(callingUid,
+ connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error findAccessibilityNodeInfoByViewId().");
@@ -463,36 +463,36 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
- if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
- return false;
+ return null;
}
@Override
- public boolean findAccessibilityNodeInfosByText(int accessibilityWindowId,
+ public String[] findAccessibilityNodeInfosByText(int accessibilityWindowId,
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
- return false;
+ return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
- return false;
+ return null;
} else {
connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
if (connection == null) {
- return false;
+ return null;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
@@ -505,12 +505,14 @@
final int interrogatingPid = Binder.getCallingPid();
callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
+ final int callingUid = Binder.getCallingUid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
- partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
+ text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
interrogatingPid, interrogatingTid, spec);
- return true;
+ return mSecurityPolicy.computeValidReportedPackages(callingUid,
+ connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfosByText()");
@@ -518,36 +520,36 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
- if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
- return false;
+ return null;
}
@Override
- public boolean findAccessibilityNodeInfoByAccessibilityId(
+ public String[] findAccessibilityNodeInfoByAccessibilityId(
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
if (!isCalledForCurrentUserLocked()) {
- return false;
+ return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
- return false;
+ return null;
} else {
connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
if (connection == null) {
- return false;
+ return null;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
@@ -560,12 +562,14 @@
final int interrogatingPid = Binder.getCallingPid();
callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
+ final int callingUid = Binder.getCallingUid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
- partialInteractiveRegion, interactionId, callback, mFetchFlags | flags,
- interrogatingPid, interrogatingTid, spec, arguments);
- return true;
+ connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
+ accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
+ mFetchFlags | flags, interrogatingPid, interrogatingTid, spec, arguments);
+ return mSecurityPolicy.computeValidReportedPackages(callingUid,
+ connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findAccessibilityNodeInfoByAccessibilityId()");
@@ -573,36 +577,36 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
- if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
- return false;
+ return null;
}
@Override
- public boolean findFocus(int accessibilityWindowId, long accessibilityNodeId,
+ public String[] findFocus(int accessibilityWindowId, long accessibilityNodeId,
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
- return false;
+ return null;
}
resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
accessibilityWindowId, focusType);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
- return false;
+ return null;
} else {
connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
if (connection == null) {
- return false;
+ return null;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
@@ -615,12 +619,14 @@
final int interrogatingPid = Binder.getCallingPid();
callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
+ final int callingUid = Binder.getCallingUid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.findFocus(accessibilityNodeId, focusType, partialInteractiveRegion,
- interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
- spec);
- return true;
+ connection.getRemote().findFocus(accessibilityNodeId, focusType,
+ partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ interrogatingPid, interrogatingTid, spec);
+ return mSecurityPolicy.computeValidReportedPackages(callingUid,
+ connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling findFocus()");
@@ -628,35 +634,35 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
- if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
- return false;
+ return null;
}
@Override
- public boolean focusSearch(int accessibilityWindowId, long accessibilityNodeId,
+ public String[] focusSearch(int accessibilityWindowId, long accessibilityNodeId,
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
- return false;
+ return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
- return false;
+ return null;
} else {
connection = mSystemSupport.getConnectionLocked(resolvedWindowId);
if (connection == null) {
- return false;
+ return null;
}
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
@@ -669,12 +675,14 @@
final int interrogatingPid = Binder.getCallingPid();
callback = mSystemSupport.replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
+ final int callingUid = Binder.getCallingUid();
final long identityToken = Binder.clearCallingIdentity();
try {
- connection.focusSearch(accessibilityNodeId, direction, partialInteractiveRegion,
- interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
- spec);
- return true;
+ connection.getRemote().focusSearch(accessibilityNodeId, direction,
+ partialInteractiveRegion, interactionId, callback, mFetchFlags,
+ interrogatingPid, interrogatingTid, spec);
+ return mSecurityPolicy.computeValidReportedPackages(callingUid,
+ connection.getPackageName(), connection.getUid());
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling accessibilityFocusSearch()");
@@ -682,11 +690,11 @@
} finally {
Binder.restoreCallingIdentity(identityToken);
// Recycle if passed to another process.
- if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ if (partialInteractiveRegion != null && Binder.isProxy(connection.getRemote())) {
partialInteractiveRegion.recycle();
}
}
- return false;
+ return null;
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0a21b9e..ac0cdd7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -24,12 +24,12 @@
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
-import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AlertDialog;
import android.app.PendingIntent;
+import android.appwidget.AppWidgetManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -72,6 +72,7 @@
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -97,6 +98,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
@@ -104,6 +106,7 @@
import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
import com.android.server.wm.WindowManagerInternal;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
@@ -186,6 +189,8 @@
private final WindowManagerInternal mWindowManagerService;
+ private AppWidgetManagerInternal mAppWidgetService;
+
private final SecurityPolicy mSecurityPolicy;
private final MainHandler mMainHandler;
@@ -218,10 +223,10 @@
private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
new RemoteCallbackList<>();
- private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections =
+ private final SparseArray<RemoteAccessibilityConnection> mGlobalInteractionConnections =
new SparseArray<>();
- private AccessibilityConnectionWrapper mPictureInPictureActionReplacingConnection;
+ private RemoteAccessibilityConnection mPictureInPictureActionReplacingConnection;
private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<>();
@@ -453,6 +458,7 @@
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+
// If the client is from a process that runs across users such as
// the system UI or the system we add it to the global state that
// is shared across users.
@@ -497,9 +503,14 @@
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
- // performs the current profile parent resolution..
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+
+ // Make sure the reported package is one the caller has access to.
+ event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
+ event.getPackageName(), UserHandle.getCallingAppId(), resolvedUserId));
+
// This method does nothing for a background user.
if (resolvedUserId == mCurrentUserId) {
if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
@@ -617,30 +628,38 @@
@Override
public int addAccessibilityInteractionConnection(IWindow windowToken,
- IAccessibilityInteractionConnection connection, int userId) throws RemoteException {
+ IAccessibilityInteractionConnection connection, String packageName,
+ int userId) throws RemoteException {
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ final int resolvedUid = UserHandle.getUid(resolvedUserId, UserHandle.getCallingAppId());
+
+ // Make sure the reported package is one the caller has access to.
+ packageName = mSecurityPolicy.resolveValidReportedPackageLocked(
+ packageName, UserHandle.getCallingAppId(), resolvedUserId);
+
final int windowId = sNextWindowId++;
// If the window is from a process that runs across users such as
// the system UI or the system we add it to the global state that
// is shared across users.
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
- AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
- windowId, connection, UserHandle.USER_ALL);
+ RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
+ windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL);
wrapper.linkToDeath();
mGlobalInteractionConnections.put(windowId, wrapper);
mGlobalWindowTokens.put(windowId, windowToken.asBinder());
if (DEBUG) {
Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid()
- + " with windowId: " + windowId + " and token: " + windowToken.asBinder());
+ + " with windowId: " + windowId + " and token: "
+ + windowToken.asBinder());
}
} else {
- AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
- windowId, connection, resolvedUserId);
+ RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
+ windowId, connection, packageName, resolvedUid, resolvedUserId);
wrapper.linkToDeath();
UserState userState = getUserStateLocked(resolvedUserId);
userState.mInteractionConnections.put(windowId, wrapper);
@@ -693,13 +712,13 @@
private int removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken,
SparseArray<IBinder> windowTokens,
- SparseArray<AccessibilityConnectionWrapper> interactionConnections) {
+ SparseArray<RemoteAccessibilityConnection> interactionConnections) {
final int count = windowTokens.size();
for (int i = 0; i < count; i++) {
if (windowTokens.valueAt(i) == windowToken) {
final int windowId = windowTokens.keyAt(i);
windowTokens.removeAt(i);
- AccessibilityConnectionWrapper wrapper = interactionConnections.get(windowId);
+ RemoteAccessibilityConnection wrapper = interactionConnections.get(windowId);
wrapper.unlinkToDeath();
interactionConnections.remove(windowId);
return windowId;
@@ -719,9 +738,9 @@
mPictureInPictureActionReplacingConnection = null;
}
if (connection != null) {
- AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
+ RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID,
- connection, UserHandle.USER_ALL);
+ connection, "foo.bar.baz", Process.SYSTEM_UID, UserHandle.USER_ALL);
mPictureInPictureActionReplacingConnection = wrapper;
wrapper.linkToDeath();
}
@@ -2264,18 +2283,35 @@
}
}
- private class AccessibilityConnectionWrapper implements DeathRecipient {
+ class RemoteAccessibilityConnection implements DeathRecipient {
+ private final int mUid;
+ private final String mPackageName;
private final int mWindowId;
private final int mUserId;
private final IAccessibilityInteractionConnection mConnection;
- public AccessibilityConnectionWrapper(int windowId,
- IAccessibilityInteractionConnection connection, int userId) {
+ RemoteAccessibilityConnection(int windowId,
+ IAccessibilityInteractionConnection connection,
+ String packageName, int uid, int userId) {
mWindowId = windowId;
+ mPackageName = packageName;
+ mUid = uid;
mUserId = userId;
mConnection = connection;
}
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public IAccessibilityInteractionConnection getRemote() {
+ return mConnection;
+ }
+
public void linkToDeath() throws RemoteException {
mConnection.asBinder().linkToDeath(this, 0);
}
@@ -2532,11 +2568,13 @@
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int fetchFlags,
long interrogatingTid) {
- IAccessibilityInteractionConnection connection = null;
+ RemoteAccessibilityConnection connection;
IBinder activityToken = null;
synchronized (mLock) {
connection = getConnectionLocked(resolvedWindowId);
- if (connection == null) return false;
+ if (connection == null) {
+ return false;
+ }
final boolean isA11yFocusAction = (action == ACTION_ACCESSIBILITY_FOCUS)
|| (action == ACTION_CLEAR_ACCESSIBILITY_FOCUS);
final AccessibilityWindowInfo a11yWindowInfo =
@@ -2548,7 +2586,7 @@
}
if ((a11yWindowInfo != null) && a11yWindowInfo.isInPictureInPictureMode()
&& (mPictureInPictureActionReplacingConnection != null) && !isA11yFocusAction) {
- connection = mPictureInPictureActionReplacingConnection.mConnection;
+ connection = mPictureInPictureActionReplacingConnection;
}
}
final int interrogatingPid = Binder.getCallingPid();
@@ -2563,8 +2601,9 @@
LocalServices.getService(ActivityManagerInternal.class)
.setFocusedActivity(activityToken);
}
- connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
- interactionId, callback, fetchFlags, interrogatingPid, interrogatingTid);
+ connection.mConnection.performAccessibilityAction(accessibilityNodeId, action,
+ arguments, interactionId, callback, fetchFlags, interrogatingPid,
+ interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling performAccessibilityAction: " + re);
@@ -2577,17 +2616,17 @@
}
@Override
- public IAccessibilityInteractionConnection getConnectionLocked(int windowId) {
+ public RemoteAccessibilityConnection getConnectionLocked(int windowId) {
if (DEBUG) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
}
- AccessibilityManagerService.AccessibilityConnectionWrapper wrapper =
+ RemoteAccessibilityConnection connection =
mGlobalInteractionConnections.get(windowId);
- if (wrapper == null) {
- wrapper = getCurrentUserStateLocked().mInteractionConnections.get(windowId);
+ if (connection == null) {
+ connection = getCurrentUserStateLocked().mInteractionConnections.get(windowId);
}
- if (wrapper != null && wrapper.mConnection != null) {
- return wrapper.mConnection;
+ if (connection != null && connection.mConnection != null) {
+ return connection;
}
if (DEBUG) {
Slog.e(LOG_TAG, "No interaction connection to window: " + windowId);
@@ -2620,6 +2659,16 @@
}
}
+ private AppWidgetManagerInternal getAppWidgetManager() {
+ synchronized (mLock) {
+ if (mAppWidgetService == null
+ && mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ mAppWidgetService = LocalServices.getService(AppWidgetManagerInternal.class);
+ }
+ return mAppWidgetService;
+ }
+ }
+
final class WindowsForAccessibilityCallback implements
WindowManagerInternal.WindowsForAccessibilityCallback {
@@ -2912,6 +2961,78 @@
}
}
+ private boolean isValidPackageForUid(String packageName, int uid) {
+ try {
+ return uid == mPackageManager.getPackageUidAsUser(
+ packageName, UserHandle.getUserId(uid));
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ String resolveValidReportedPackageLocked(CharSequence packageName, int appId, int userId) {
+ // Okay to pass no package
+ if (packageName == null) {
+ return null;
+ }
+ // The system gets to pass any package
+ if (appId == Process.SYSTEM_UID) {
+ return packageName.toString();
+ }
+ // Passing a package in your UID is fine
+ final String packageNameStr = packageName.toString();
+ final int resolvedUid = UserHandle.getUid(userId, appId);
+ if (isValidPackageForUid(packageNameStr, resolvedUid)) {
+ return packageName.toString();
+ }
+ // Appwidget hosts get to pass packages for widgets they host
+ final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
+ if (appWidgetManager != null && ArrayUtils.contains(appWidgetManager
+ .getHostedWidgetPackages(resolvedUid), packageNameStr)) {
+ return packageName.toString();
+ }
+ // Otherwise, set the package to the first one in the UID
+ final String[] packageNames = mPackageManager.getPackagesForUid(resolvedUid);
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return null;
+ }
+ // Okay, the caller reported a package it does not have access to.
+ // Instead of crashing the caller for better backwards compatibility
+ // we report the first package in the UID. Since most of the time apps
+ // don't use shared user id, this will yield correct results and for
+ // the edge case of using a shared user id we may report the wrong
+ // package but this is fine since first, this is a cheating app and
+ // second there is no way to get the correct package anyway.
+ return packageNames[0];
+ }
+
+ String[] computeValidReportedPackages(int callingUid,
+ String targetPackage, int targetUid) {
+ if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
+ // Empty array means any package is Okay
+ return EmptyArray.STRING;
+ }
+ // IMPORTANT: The target package is already vetted to be in the target UID
+ String[] uidPackages = new String[]{targetPackage};
+ // Appwidget hosts get to pass packages for widgets they host
+ final AppWidgetManagerInternal appWidgetManager = getAppWidgetManager();
+ if (appWidgetManager != null) {
+ final ArraySet<String> widgetPackages = appWidgetManager
+ .getHostedWidgetPackages(targetUid);
+ if (widgetPackages != null && !widgetPackages.isEmpty()) {
+ final String[] validPackages = new String[uidPackages.length
+ + widgetPackages.size()];
+ System.arraycopy(uidPackages, 0, validPackages, 0, uidPackages.length);
+ final int widgetPackageCount = widgetPackages.size();
+ for (int i = 0; i < widgetPackageCount; i++) {
+ validPackages[uidPackages.length + i] = widgetPackages.valueAt(i);
+ }
+ return validPackages;
+ }
+ }
+ return uidPackages;
+ }
+
public void clearWindowsLocked() {
List<WindowInfo> windows = Collections.emptyList();
final int activeWindowId = mActiveWindowId;
@@ -3338,7 +3459,7 @@
public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
new RemoteCallbackList<>();
- public final SparseArray<AccessibilityConnectionWrapper> mInteractionConnections =
+ public final SparseArray<RemoteAccessibilityConnection> mInteractionConnections =
new SparseArray<>();
public final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b446209..54cf726 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -32,6 +32,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetManagerInternal;
import android.appwidget.AppWidgetProviderInfo;
import android.appwidget.PendingHostUpdate;
import android.content.BroadcastReceiver;
@@ -100,6 +101,8 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsFactory;
@@ -107,6 +110,7 @@
import com.android.server.WidgetBackupProvider;
import com.android.server.policy.IconUtilities;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -259,6 +263,8 @@
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
registerOnCrossProfileProvidersChangedListener();
+
+ LocalServices.addService(AppWidgetManagerInternal.class, new AppWidgetManagerLocal());
}
private void computeMaximumWidgetBitmapMemory() {
@@ -4630,4 +4636,24 @@
}
}
}
+
+ private class AppWidgetManagerLocal extends AppWidgetManagerInternal {
+ @Override
+ public ArraySet<String> getHostedWidgetPackages(int uid) {
+ synchronized (mLock) {
+ ArraySet<String> widgetPackages = null;
+ final int widgetCount = mWidgets.size();
+ for (int i = 0; i < widgetCount; i++) {
+ final Widget widget = mWidgets.get(i);
+ if (widget.host.id.uid == uid) {
+ if (widgetPackages == null) {
+ widgetPackages = new ArraySet<>();
+ }
+ widgetPackages.add(widget.provider.id.componentName.getPackageName());
+ }
+ }
+ return widgetPackages;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index 08392b0..c86042b 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -22,8 +22,8 @@
import android.os.Trace;
/**
- * Thread for handling all window animations, or anything that's directly impacting animations like
- * starting windows or traversals.
+ * Thread for handling all legacy window animations, or anything that's directly impacting
+ * animations like starting windows or traversals.
*/
public final class AnimationThread extends ServiceThread {
private static AnimationThread sInstance;
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index 28c3585..bd2e96e 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -51,6 +51,7 @@
// Information about the current status of the default network.
@GuardedBy("this")
private DefaultNetworkEvent mCurrentDefaultNetwork;
+ // True if the current default network has been validated.
@GuardedBy("this")
private boolean mIsCurrentlyValid;
@GuardedBy("this")
@@ -71,6 +72,8 @@
printEvent(localTimeMs, pw, ev);
}
mCurrentDefaultNetwork.updateDuration(timeMs);
+ // When printing default network events for bug reports, update validation time
+ // and refresh the last validation timestmap for future validation time updates.
if (mIsCurrentlyValid) {
updateValidationTime(timeMs);
mLastValidationTimeMs = timeMs;
@@ -92,11 +95,13 @@
}
public synchronized void logDefaultNetworkValidity(long timeMs, boolean isValid) {
+ // Transition from valid to invalid: update validity duration since last update
if (!isValid && mIsCurrentlyValid) {
mIsCurrentlyValid = false;
updateValidationTime(timeMs);
}
+ // Transition from invalid to valid: simply mark the validation timestamp.
if (isValid && !mIsCurrentlyValid) {
mIsCurrentlyValid = true;
mLastValidationTimeMs = timeMs;
@@ -114,6 +119,9 @@
}
private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) {
+ if (mIsCurrentlyValid) {
+ updateValidationTime(timeMs);
+ }
DefaultNetworkEvent ev = mCurrentDefaultNetwork;
ev.updateDuration(timeMs);
ev.previousTransports = mLastTransports;
@@ -122,7 +130,6 @@
// The system acquired a new default network.
fillLinkInfo(ev, oldNai);
ev.finalScore = oldNai.getCurrentScore();
- ev.validatedMs = ev.durationMs;
}
// Only change transport of the previous default network if the event currently logged
// corresponds to an existing default network, and not to the absence of a default network.
@@ -143,9 +150,10 @@
fillLinkInfo(ev, newNai);
ev.initialScore = newNai.getCurrentScore();
if (newNai.lastValidated) {
- mIsCurrentlyValid = true;
- mLastValidationTimeMs = timeMs;
+ logDefaultNetworkValidity(timeMs, true);
}
+ } else {
+ mIsCurrentlyValid = false;
}
mCurrentDefaultNetwork = ev;
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 19a74d7..c1bfa47 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -76,6 +76,7 @@
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.SurfaceAnimationThread;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -292,6 +293,8 @@
Process.THREAD_GROUP_TOP_APP);
Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
Process.THREAD_GROUP_TOP_APP);
+ Process.setThreadGroupAndCpuset(SurfaceAnimationThread.get().getThreadId(),
+ Process.THREAD_GROUP_TOP_APP);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0f3ec7..5dfb48a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12884,14 +12884,13 @@
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return null;
}
- // writer
- synchronized (mPackages) {
- if (!isExternalMediaAvailable()) {
+ if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
// the caller may not have been able to delete all of this
// packages files and can not delete any more. Bail.
- return null;
- }
+ return null;
+ }
+ synchronized (mPackages) {
final ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned;
if (lastPackage != null) {
pkgs.remove(lastPackage);
diff --git a/services/core/java/com/android/server/timezone/ClockHelper.java b/services/core/java/com/android/server/timezone/ClockHelper.java
deleted file mode 100644
index 353728a..0000000
--- a/services/core/java/com/android/server/timezone/ClockHelper.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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.server.timezone;
-
-/**
- * An easy-to-mock interface for obtaining a monotonically increasing time value in milliseconds.
- */
-interface ClockHelper {
-
- long currentTimestamp();
-}
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index 1c54320..c362c80 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -22,11 +22,14 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Environment;
+import android.os.FileUtils;
+import android.os.SystemClock;
import android.provider.TimeZoneRulesDataContract;
import android.util.Slog;
import java.io.File;
import java.io.PrintWriter;
+import java.time.Clock;
/**
* Monitors the installed applications associated with time zone updates. If the app packages are
@@ -58,7 +61,7 @@
private final IntentHelper mIntentHelper;
private final ConfigHelper mConfigHelper;
private final PackageStatusStorage mPackageStatusStorage;
- private final ClockHelper mClockHelper;
+ private final Clock mElapsedRealtimeClock;
// False if tracking is disabled.
private boolean mTrackingEnabled;
@@ -91,15 +94,15 @@
/** Creates the {@link PackageTracker} for normal use. */
static PackageTracker create(Context context) {
+ Clock elapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context);
- // TODO(nfuller): Switch to FileUtils.createDir() when available. http://b/31008728
- File storageDir = new File(Environment.getDataSystemDirectory(), "timezone");
+ File storageDir = FileUtils.createDir(Environment.getDataSystemDirectory(), "timezone");
if (!storageDir.exists()) {
storageDir.mkdir();
}
return new PackageTracker(
- helperImpl /* clock */,
+ elapsedRealtimeClock /* elapsedRealtimeClock */,
helperImpl /* configHelper */,
helperImpl /* packageManagerHelper */,
new PackageStatusStorage(storageDir),
@@ -107,10 +110,10 @@
}
// A constructor that can be used by tests to supply mocked / faked dependencies.
- PackageTracker(ClockHelper clockHelper, ConfigHelper configHelper,
+ PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper,
PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage,
IntentHelper intentHelper) {
- mClockHelper = clockHelper;
+ mElapsedRealtimeClock = elapsedRealtimeClock;
mConfigHelper = configHelper;
mPackageManagerHelper = packageManagerHelper;
mPackageStatusStorage = packageStatusStorage;
@@ -425,7 +428,7 @@
}
private void setCheckInProgress() {
- mLastTriggerTimestamp = mClockHelper.currentTimestamp();
+ mLastTriggerTimestamp = mElapsedRealtimeClock.millis();
}
private void setCheckComplete() {
@@ -441,7 +444,7 @@
return false;
}
// Risk of overflow, but highly unlikely given the implementation and not problematic.
- return mClockHelper.currentTimestamp() > mLastTriggerTimestamp + mCheckTimeAllowedMillis;
+ return mElapsedRealtimeClock.millis() > mLastTriggerTimestamp + mCheckTimeAllowedMillis;
}
private PackageVersions lookupInstalledPackageVersions() {
diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
index 6a330e6..5f90be1 100644
--- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -25,7 +25,6 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
@@ -34,7 +33,7 @@
/**
* A single class that implements multiple helper interfaces for use by {@link PackageTracker}.
*/
-final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, PackageManagerHelper {
+final class PackageTrackerHelperImpl implements ConfigHelper, PackageManagerHelper {
private static final String TAG = "PackageTrackerHelperImpl";
@@ -74,13 +73,6 @@
}
@Override
- public long currentTimestamp() {
- // Use of elapsedRealtime() because this is in-memory state and elapsedRealtime() shouldn't
- // change if the system clock changes.
- return SystemClock.elapsedRealtime();
- }
-
- @Override
public long getInstalledPackageVersion(String packageName)
throws PackageManager.NameNotFoundException {
int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
index 0cf61c0..e8a401e 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
@@ -16,6 +16,8 @@
package com.android.server.timezone;
+import com.android.internal.util.DumpUtils;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
@@ -46,15 +48,7 @@
@Override
public boolean checkDumpPermission(String tag, PrintWriter pw) {
- // TODO(nfuller): Switch to DumpUtils.checkDumpPermission() when it is available in AOSP.
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump LocationManagerService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return false;
- }
- return true;
+ return DumpUtils.checkDumpPermission(mContext, tag, pw);
}
@Override
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
new file mode 100644
index 0000000..84d47b4
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -0,0 +1,69 @@
+/*
+ * 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.server.wm;
+
+import android.annotation.ColorInt;
+import android.graphics.Point;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+/**
+ * Interface that describes an animation and bridges the animation start to the component
+ * responsible for running the animation.
+ */
+interface AnimationAdapter {
+
+ /**
+ * @return Whether we should detach the wallpaper during the animation.
+ * @see Animation#setDetachWallpaper
+ */
+ boolean getDetachWallpaper();
+
+ /**
+ * @return The background color behind the animation.
+ */
+ @ColorInt int getBackgroundColor();
+
+ /**
+ * Requests to start the animation.
+ *
+ * @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
+ * overview of the mechanism. This surface needs to be released by the
+ * component running the animation after {@code finishCallback} has been
+ * invoked, or after the animation was cancelled.
+ * @param t The Transaction to apply the initial frame of the animation.
+ * @param finishCallback The callback to be invoked when the animation has finished.
+ */
+ void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback);
+
+ /**
+ * Called when the animation that was started with {@link #startAnimation} was cancelled by the
+ * window manager.
+ *
+ * @param animationLeash The leash passed to {@link #startAnimation}.
+ */
+ void onAnimationCancelled(SurfaceControl animationLeash);
+
+ /**
+ * @return The approximate duration of the animation, in milliseconds.
+ */
+ long getDurationHint();
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 91cad46..eda8fec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1766,8 +1766,8 @@
updateBounds();
}
- void getContentRect(Rect out) {
- out.set(mDisplayFrames.mContent);
+ void getStableRect(Rect out) {
+ out.set(mDisplayFrames.mStable);
}
TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 7e29a3a..88b7a11 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -390,12 +390,13 @@
}
}
- final boolean inPositioning = (mService.mTaskPositioner != null);
+ final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
if (inPositioning) {
if (DEBUG_TASK_POSITIONING) {
Log.d(TAG_WM, "Inserting window handle for repositioning");
}
- final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
+ final InputWindowHandle dragWindowHandle =
+ mService.mTaskPositioningController.getDragWindowHandleLocked();
if (dragWindowHandle != null) {
addInputWindowHandle(dragWindowHandle);
} else {
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
new file mode 100644
index 0000000..5fe4565
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -0,0 +1,100 @@
+/*
+ * 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.server.wm;
+
+import android.graphics.Point;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+/**
+ * Animation that can be executed without holding the window manager lock. See
+ * {@link SurfaceAnimationRunner}.
+ */
+class LocalAnimationAdapter implements AnimationAdapter {
+
+ private final AnimationSpec mSpec;
+ private final SurfaceAnimationRunner mAnimator;
+
+ LocalAnimationAdapter(AnimationSpec spec, SurfaceAnimationRunner animator) {
+ mSpec = spec;
+ mAnimator = animator;
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return mSpec.getDetachWallpaper();
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return mSpec.getBackgroundColor();
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, Transaction t,
+ OnAnimationFinishedCallback finishCallback) {
+ mAnimator.startAnimation(mSpec, animationLeash, t,
+ () -> finishCallback.onAnimationFinished(this));
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ mAnimator.onAnimationCancelled(animationLeash);
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mSpec.getDuration();
+ }
+
+ /**
+ * Describes how to apply an animation.
+ */
+ interface AnimationSpec {
+
+ /**
+ * @see AnimationAdapter#getDetachWallpaper
+ */
+ default boolean getDetachWallpaper() {
+ return false;
+ }
+
+ /**
+ * @see AnimationAdapter#getBackgroundColor
+ */
+ default int getBackgroundColor() {
+ return 0;
+ }
+
+ /**
+ * @return The duration of the animation.
+ */
+ long getDuration();
+
+ /**
+ * Called when the spec needs to apply the current animation state to the leash.
+ *
+ * @param t The transaction to use to apply a transform.
+ * @param leash The leash to apply the state to.
+ * @param currentPlayTime The current time of the animation.
+ */
+ void apply(Transaction t, SurfaceControl leash, long currentPlayTime);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 63eea10..192d6c8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -368,7 +368,7 @@
long ident = Binder.clearCallingIdentity();
try {
- return mService.startMovingTask(window, startX, startY);
+ return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
new file mode 100644
index 0000000..5bc2722
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -0,0 +1,217 @@
+/*
+ * 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.server.wm;
+
+import static android.view.Choreographer.CALLBACK_TRAVERSAL;
+import static android.view.Choreographer.getSfInstance;
+
+import android.animation.AnimationHandler;
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.Transformation;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.server.AnimationThread;
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+
+/**
+ * Class to run animations without holding the window manager lock.
+ */
+class SurfaceAnimationRunner {
+
+ private final Object mLock = new Object();
+
+ @VisibleForTesting
+ Choreographer mChoreographer;
+
+ private final Runnable mApplyTransactionRunnable = this::applyTransaction;
+ private final AnimationHandler mAnimationHandler;
+ private final Transaction mFrameTransaction;
+ private boolean mApplyScheduled;
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final ArrayMap<SurfaceControl, ValueAnimator> mRunningAnimations = new ArrayMap<>();
+
+ SurfaceAnimationRunner() {
+ this(null /* callbackProvider */, new Transaction());
+ }
+
+ @VisibleForTesting
+ SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
+ Transaction frameTransaction) {
+ SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
+ 0 /* timeout */);
+ mFrameTransaction = frameTransaction;
+ mAnimationHandler = new AnimationHandler();
+ mAnimationHandler.setProvider(callbackProvider != null
+ ? callbackProvider
+ : new SfVsyncFrameCallbackProvider(mChoreographer));
+ }
+
+ void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
+ Runnable finishCallback) {
+ synchronized (mLock) {
+ final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
+ finishCallback);
+ mPendingAnimations.put(animationLeash, runningAnim);
+ mChoreographer.postFrameCallback(this::stepAnimation);
+
+ // Some animations (e.g. move animations) require the initial transform to be applied
+ // immediately.
+ applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
+ }
+ }
+
+ void onAnimationCancelled(SurfaceControl leash) {
+ synchronized (mLock) {
+ if (mPendingAnimations.containsKey(leash)) {
+ mPendingAnimations.remove(leash);
+ // TODO: Releasing the leash is problematic if reparenting hasn't happened yet.
+ // Fix with transaction
+ //leash.release();
+ return;
+ }
+ final ValueAnimator anim = mRunningAnimations.get(leash);
+ if (anim != null) {
+ mRunningAnimations.remove(leash);
+ SurfaceAnimationThread.getHandler().post(() -> {
+ anim.cancel();
+ applyTransaction();
+ //leash.release();
+ });
+ }
+ }
+ }
+
+ private void startPendingAnimationsLocked() {
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ startAnimationLocked(mPendingAnimations.valueAt(i));
+ }
+ mPendingAnimations.clear();
+ }
+
+ private void startAnimationLocked(RunningAnimation a) {
+ final ValueAnimator result = new SfValueAnimator();
+
+ // Animation length is already expected to be scaled.
+ result.overrideDurationScale(1.0f);
+ result.setDuration(a.animSpec.getDuration());
+ result.addUpdateListener(animation -> {
+ applyTransformation(a, mFrameTransaction, result.getCurrentPlayTime());
+
+ // Transaction will be applied in the commit phase.
+ scheduleApplyTransaction();
+ });
+ result.addListener(new AnimatorListenerAdapter() {
+
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mFrameTransaction.show(a.leash);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ synchronized (mLock) {
+ mRunningAnimations.remove(a.leash);
+ }
+ if (!mCancelled) {
+ // Post on other thread that we can push final state without jank.
+ AnimationThread.getHandler().post(() -> {
+ a.finishCallback.run();
+
+ // Make sure to release the leash after finishCallback has been invoked such
+ // that reparenting is done already when releasing the leash.
+ a.leash.release();
+ });
+ }
+ }
+ });
+ result.start();
+ mRunningAnimations.put(a.leash, result);
+ }
+
+ private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
+ a.animSpec.apply(t, a.leash, currentPlayTime);
+ }
+
+ private void stepAnimation(long frameTimeNanos) {
+ synchronized (mLock) {
+ startPendingAnimationsLocked();
+ }
+ }
+
+ private void scheduleApplyTransaction() {
+ if (!mApplyScheduled) {
+ mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
+ null /* token */);
+ mApplyScheduled = true;
+ }
+ }
+
+ private void applyTransaction() {
+ mFrameTransaction.apply();
+ mApplyScheduled = false;
+ }
+
+ private static final class RunningAnimation {
+ final AnimationSpec animSpec;
+ final SurfaceControl leash;
+ final Runnable finishCallback;
+
+ RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) {
+ this.animSpec = animSpec;
+ this.leash = leash;
+ this.finishCallback = finishCallback;
+ }
+ }
+
+ /**
+ * Value animator that uses sf-vsync signal to tick.
+ */
+ private class SfValueAnimator extends ValueAnimator {
+
+ SfValueAnimator() {
+ setFloatValues(0f, 1f);
+ }
+
+ @Override
+ public AnimationHandler getAnimationHandler() {
+ return mAnimationHandler;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationThread.java b/services/core/java/com/android/server/wm/SurfaceAnimationThread.java
new file mode 100644
index 0000000..0d3afc0
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationThread.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.server.wm;
+
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+
+import android.os.Handler;
+import android.os.Trace;
+
+import com.android.server.ServiceThread;
+
+/**
+ * Thread for running {@link SurfaceAnimationRunner} that does not hold the window manager lock.
+ */
+public final class SurfaceAnimationThread extends ServiceThread {
+ private static SurfaceAnimationThread sInstance;
+ private static Handler sHandler;
+
+ private SurfaceAnimationThread() {
+ super("android.anim.lf", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new SurfaceAnimationThread();
+ sInstance.start();
+ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ public static SurfaceAnimationThread get() {
+ synchronized (SurfaceAnimationThread.class) {
+ ensureThreadLocked();
+ return sInstance;
+ }
+ }
+
+ public static Handler getHandler() {
+ synchronized (SurfaceAnimationThread.class) {
+ ensureThreadLocked();
+ return sHandler;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
new file mode 100644
index 0000000..713d58b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -0,0 +1,311 @@
+/*
+ * 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.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+
+import java.io.PrintWriter;
+
+/**
+ * A class that can run animations on objects that have a set of child surfaces. We do this by
+ * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
+ * gets attached in the surface hierarchy where the the children were attached to. We then hand off
+ * the Leash to the component handling the animation, which is specified by the
+ * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
+ * animation will be invoked, at which we reparent the children back to the original parent.
+ */
+class SurfaceAnimator {
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM;
+ private final WindowManagerService mService;
+ private AnimationAdapter mAnimation;
+ private SurfaceControl mLeash;
+ private final Animatable mAnimatable;
+ private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
+ private final Runnable mAnimationFinishedCallback;
+ private boolean mAnimationStartDelayed;
+
+ /**
+ * @param animatable The object to animate.
+ * @param animationFinishedCallback Callback to invoke when an animation has finished running.
+ */
+ SurfaceAnimator(Animatable animatable, Runnable animationFinishedCallback,
+ WindowManagerService service) {
+ mAnimatable = animatable;
+ mService = service;
+ mAnimationFinishedCallback = animationFinishedCallback;
+ mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback);
+ }
+
+ private OnAnimationFinishedCallback getFinishedCallback(Runnable animationFinishedCallback) {
+ return anim -> {
+ synchronized (mService.mWindowMap) {
+ if (anim != mAnimation) {
+ // Callback was from another animation - ignore.
+ return;
+ }
+
+ final Transaction t = new Transaction();
+ SurfaceControl.openTransaction();
+ try {
+ reset(t);
+ animationFinishedCallback.run();
+ } finally {
+ // TODO: This should use pendingTransaction eventually, but right now things
+ // happening on the animation finished callback are happening on the global
+ // transaction.
+ SurfaceControl.mergeToGlobalTransaction(t);
+ SurfaceControl.closeTransaction();
+ }
+ }
+ };
+ }
+
+ /**
+ * Starts an animation.
+ *
+ * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
+ * component responsible for running the animation. It runs the animation with
+ * {@link AnimationAdapter#startAnimation} once the hierarchy with
+ * the Leash has been set up.
+ * @param hidden Whether the container holding the child surfaces is currently visible or not.
+ * This is important as it will start with the leash hidden or visible before
+ * handing it to the component that is responsible to run the animation.
+ */
+ void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
+ cancelAnimation(t, true /* restarting */);
+ mAnimation = anim;
+ final SurfaceControl surface = mAnimatable.getSurface();
+ if (surface == null) {
+ Slog.w(TAG, "Unable to start animation, surface is null or no children.");
+ cancelAnimation();
+ return;
+ }
+ mLeash = createAnimationLeash(surface, t,
+ mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden);
+ mAnimatable.onLeashCreated(t, mLeash);
+ if (mAnimationStartDelayed) {
+ if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
+ return;
+ }
+ mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback);
+ }
+
+ /**
+ * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation}
+ * will not start the animation until {@link #endDelayingAnimationStart} is called. When an
+ * animation start is being delayed, the animator is considered animating already.
+ */
+ void startDelayingAnimationStart() {
+
+ // We only allow delaying animation start we are not currently animating
+ if (!isAnimating()) {
+ mAnimationStartDelayed = true;
+ }
+ }
+
+ /**
+ * See {@link #startDelayingAnimationStart}.
+ */
+ void endDelayingAnimationStart() {
+ final boolean delayed = mAnimationStartDelayed;
+ mAnimationStartDelayed = false;
+ if (delayed && mAnimation != null) {
+ mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(),
+ mInnerAnimationFinishedCallback);
+ mAnimatable.commitPendingTransaction();
+ }
+ }
+
+ /**
+ * @return Whether we are currently running an animation, or we have a pending animation that
+ * is waiting to be started with {@link #endDelayingAnimationStart}
+ */
+ boolean isAnimating() {
+ return mAnimation != null;
+ }
+
+ /**
+ * @return The current animation spec if we are running an animation, or {@code null} otherwise.
+ */
+ AnimationAdapter getAnimation() {
+ return mAnimation;
+ }
+
+ /**
+ * Cancels any currently running animation.
+ */
+ void cancelAnimation() {
+ cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */);
+ mAnimatable.commitPendingTransaction();
+ }
+
+ /**
+ * Sets the layer of the surface.
+ * <p>
+ * When the layer of the surface needs to be adjusted, we need to set it on the leash if the
+ * surface is reparented to the leash. This method takes care of that.
+ */
+ void setLayer(Transaction t, int layer) {
+ t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurface(), layer);
+ }
+
+ /**
+ * Reparents the surface.
+ *
+ * @see #setLayer
+ */
+ void reparent(Transaction t, SurfaceControl newParent) {
+ t.reparent(mLeash != null ? mLeash : mAnimatable.getSurface(), newParent.getHandle());
+ }
+
+ /**
+ * @return True if the surface is attached to the leash; false otherwise.
+ */
+ boolean hasLeash() {
+ return mLeash != null;
+ }
+
+ private void cancelAnimation(Transaction t, boolean restarting) {
+ if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting);
+ final SurfaceControl leash = mLeash;
+ final AnimationAdapter animation = mAnimation;
+ reset(t);
+ if (animation != null) {
+ if (!mAnimationStartDelayed) {
+ animation.onAnimationCancelled(leash);
+ }
+ if (!restarting) {
+ mAnimationFinishedCallback.run();
+ }
+ }
+ if (!restarting) {
+ mAnimationStartDelayed = false;
+ }
+ }
+
+ private void reset(Transaction t) {
+ final SurfaceControl surface = mAnimatable.getSurface();
+ final SurfaceControl parent = mAnimatable.getParentSurface();
+
+ // If the surface was destroyed, we don't care to reparent it back.
+ final boolean destroy = mLeash != null && surface != null && parent != null;
+ if (destroy) {
+ if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
+ t.reparent(surface, parent.getHandle());
+ }
+ mLeash = null;
+ mAnimation = null;
+
+ // Make sure to inform the animatable after the leash was destroyed.
+ if (destroy) {
+ mAnimatable.onLeashDestroyed(t);
+ }
+ }
+
+ private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width,
+ int height, boolean hidden) {
+ if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
+ final SurfaceControl.Builder builder = mAnimatable.makeLeash()
+ .setName(surface + " - animation-leash")
+ .setSize(width, height);
+ final SurfaceControl leash = builder.build();
+ if (!hidden) {
+ t.show(leash);
+ }
+ t.reparent(surface, leash.getHandle());
+ return leash;
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mAnimation="); pw.print(mAnimation);
+ pw.print(" mLeash="); pw.println(mLeash);
+ }
+
+ /**
+ * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the
+ * component that is running the animation when the animation is finished.
+ */
+ interface OnAnimationFinishedCallback {
+ void onAnimationFinished(AnimationAdapter anim);
+ }
+
+ /**
+ * Interface to be animated by {@link SurfaceAnimator}.
+ */
+ interface Animatable {
+
+ /**
+ * @return The pending transaction that will be committed in the next frame.
+ */
+ @NonNull Transaction getPendingTransaction();
+
+ /**
+ * Schedules a commit of the pending transaction.
+ */
+ void commitPendingTransaction();
+
+ /**
+ * Called when the was created.
+ *
+ * @param t The transaction to use to apply any necessary changes.
+ * @param leash The leash that was created.
+ */
+ void onLeashCreated(Transaction t, SurfaceControl leash);
+
+ /**
+ * Called when the leash is being destroyed, and the surface was reparented back to the
+ * original parent.
+ *
+ * @param t The transaction to use to apply any necessary changes.
+ */
+ void onLeashDestroyed(Transaction t);
+
+ /**
+ * @return A new child surface.
+ */
+ SurfaceControl.Builder makeLeash();
+
+ /**
+ * @return The surface of the object to be animated.
+ */
+ @Nullable SurfaceControl getSurface();
+
+ /**
+ * @return The parent of the surface object to be animated.
+ */
+ @Nullable SurfaceControl getParentSurface();
+
+ /**
+ * @return The width of the surface to be animated.
+ */
+ int getSurfaceWidth();
+
+ /**
+ * @return The height of the surface to be animated.
+ */
+ int getSurfaceHeight();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 87d0a40..ca9f3a9 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -16,15 +16,11 @@
package com.android.server.wm;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.dipToPixel;
@@ -44,7 +40,6 @@
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
new file mode 100644
index 0000000..bb5706c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -0,0 +1,159 @@
+/*
+ * 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.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.Nullable;
+import android.app.IActivityManager;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.Display;
+import android.view.IWindow;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.input.InputManagerService;
+import com.android.server.input.InputWindowHandle;
+
+/**
+ * Controller for task positioning by drag.
+ */
+class TaskPositioningController {
+ private final WindowManagerService mService;
+ private final InputManagerService mInputManager;
+ private final InputMonitor mInputMonitor;
+ private final IActivityManager mActivityManager;
+
+ @GuardedBy("WindowManagerSerivce.mWindowMap")
+ private @Nullable TaskPositioner mTaskPositioner;
+
+ boolean isPositioningLocked() {
+ return mTaskPositioner != null;
+ }
+
+ InputWindowHandle getDragWindowHandleLocked() {
+ return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
+ }
+
+ TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
+ InputMonitor inputMonitor, IActivityManager activityManager) {
+ mService = service;
+ mInputMonitor = inputMonitor;
+ mInputManager = inputManager;
+ mActivityManager = activityManager;
+ }
+
+ boolean startMovingTask(IWindow window, float startX, float startY) {
+ WindowState win = null;
+ synchronized (mService.mWindowMap) {
+ win = mService.windowForClientLocked(null, window, false);
+ // win shouldn't be null here, pass it down to startPositioningLocked
+ // to get warning if it's null.
+ if (!startPositioningLocked(
+ win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
+ return false;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ } catch(RemoteException e) {}
+ return true;
+ }
+
+ void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
+ int taskId = -1;
+ synchronized (mService.mWindowMap) {
+ final Task task = displayContent.findTaskForResizePoint(x, y);
+ if (task != null) {
+ if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
+ task.preserveOrientationOnResize(), x, y)) {
+ return;
+ }
+ taskId = task.mTaskId;
+ } else {
+ taskId = displayContent.taskIdFromPoint(x, y);
+ }
+ }
+ if (taskId >= 0) {
+ try {
+ mActivityManager.setFocusedTask(taskId);
+ } catch(RemoteException e) {}
+ }
+ }
+
+ private boolean startPositioningLocked(WindowState win, boolean resize,
+ boolean preserveOrientation, float startX, float startY) {
+ if (DEBUG_TASK_POSITIONING)
+ Slog.d(TAG_WM, "startPositioningLocked: "
+ + "win=" + win + ", resize=" + resize + ", preserveOrientation="
+ + preserveOrientation + ", {" + startX + ", " + startY + "}");
+
+ if (win == null || win.getAppToken() == null) {
+ Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
+ return false;
+ }
+ if (win.mInputChannel == null) {
+ Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
+ + " probably being removed");
+ return false;
+ }
+
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent == null) {
+ Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
+ return false;
+ }
+
+ Display display = displayContent.getDisplay();
+ mTaskPositioner = new TaskPositioner(mService);
+ mTaskPositioner.register(displayContent);
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+
+ // We need to grab the touch focus so that the touch events during the
+ // resizing/scrolling are not sent to the app. 'win' is the main window
+ // of the app, it may not have focus since there might be other windows
+ // on top (eg. a dialog window).
+ WindowState transferFocusFromWin = win;
+ if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
+ && mService.mCurrentFocus.mAppToken == win.mAppToken) {
+ transferFocusFromWin = mService.mCurrentFocus;
+ }
+ if (!mInputManager.transferTouchFocus(
+ transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
+ Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ return false;
+ }
+
+ mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
+ return true;
+ }
+
+ void finishPositioning() {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
+
+ synchronized (mService.mWindowMap) {
+ if (mTaskPositioner != null) {
+ mTaskPositioner.unregister();
+ mTaskPositioner = null;
+ mInputMonitor.updateInputWindowsLw(true /*force*/);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 832d395..28b1390 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1114,12 +1114,12 @@
return false;
}
- final Rect displayContentRect = mTmpRect;
+ final Rect displayStableRect = mTmpRect;
final Rect contentBounds = mTmpRect2;
// Calculate the content bounds excluding the area occupied by IME
- getDisplayContent().getContentRect(displayContentRect);
- contentBounds.set(displayContentRect);
+ getDisplayContent().getStableRect(displayStableRect);
+ contentBounds.set(displayStableRect);
int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
imeTop += imeWin.getGivenContentInsetsLw().top;
@@ -1127,7 +1127,7 @@
contentBounds.bottom = imeTop;
}
- final int yOffset = displayContentRect.bottom - contentBounds.bottom;
+ final int yOffset = displayStableRect.bottom - contentBounds.bottom;
final int dividerWidth =
getDisplayContent().mDividerControllerLocked.getContentWidth();
@@ -1139,7 +1139,7 @@
// occluded by IME. We shift its bottom up by the height of the IME, but
// leaves at least 30% of the top stack visible.
final int minTopStackBottom =
- getMinTopStackBottom(displayContentRect, getRawBounds().bottom);
+ getMinTopStackBottom(displayStableRect, getRawBounds().bottom);
final int bottom = Math.max(
getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive,
minTopStackBottom);
@@ -1159,7 +1159,7 @@
final int topBeforeImeAdjust =
getRawBounds().top - dividerWidth + dividerWidthInactive;
final int minTopStackBottom =
- getMinTopStackBottom(displayContentRect,
+ getMinTopStackBottom(displayStableRect,
getRawBounds().top - dividerWidth);
final int top = Math.max(
getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive);
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
new file mode 100644
index 0000000..56175c7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -0,0 +1,70 @@
+/*
+ * 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.server.wm;
+
+import android.graphics.Point;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+
+/**
+ * Animation spec for regular window animations.
+ */
+public class WindowAnimationSpec implements AnimationSpec {
+
+ private Animation mAnimation;
+ private final Point mPosition = new Point();
+ private final ThreadLocal<Tmp> mThreadLocalTmps = ThreadLocal.withInitial(Tmp::new);
+
+ public WindowAnimationSpec(Animation animation, Point position) {
+ mAnimation = animation;
+ mPosition.set(position.x, position.y);
+ }
+
+ @Override
+ public boolean getDetachWallpaper() {
+ return mAnimation.getDetachWallpaper();
+ }
+
+ @Override
+ public int getBackgroundColor() {
+ return mAnimation.getBackgroundColor();
+ }
+
+ @Override
+ public long getDuration() {
+ return mAnimation.computeDurationHint();
+ }
+
+ @Override
+ public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
+ final Tmp tmp = mThreadLocalTmps.get();
+ tmp.transformation.clear();
+ mAnimation.getTransformation(currentPlayTime, tmp.transformation);
+ tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
+ t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
+ t.setAlpha(leash, tmp.transformation.getAlpha());
+ }
+
+ private static class Tmp {
+ final Transformation transformation = new Transformation();
+ final float[] floats = new float[9];
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e598224..8c9948e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -755,7 +755,7 @@
// Whether or not a layout can cause a wake up when theater mode is enabled.
boolean mAllowTheaterModeWakeFromLayout;
- TaskPositioner mTaskPositioner;
+ final TaskPositioningController mTaskPositioningController;
final DragDropController mDragDropController;
// For frozen screen animations.
@@ -769,6 +769,7 @@
int mTransactionSequence;
final WindowAnimator mAnimator;
+ final SurfaceAnimationRunner mSurfaceAnimationRunner;
final BoundsAnimationController mBoundsAnimationController;
@@ -1082,10 +1083,13 @@
mHoldingScreenWakeLock.setReferenceCounted(false);
mAnimator = new WindowAnimator(this);
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner();
mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
+ mTaskPositioningController =
+ new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager);
mDragDropController = new DragDropController(this, mH.getLooper());
LocalServices.addService(WindowManagerInternal.class, new LocalService());
@@ -4440,107 +4444,6 @@
}
}
- boolean startMovingTask(IWindow window, float startX, float startY) {
- WindowState win = null;
- synchronized (mWindowMap) {
- win = windowForClientLocked(null, window, false);
- // win shouldn't be null here, pass it down to startPositioningLocked
- // to get warning if it's null.
- if (!startPositioningLocked(
- win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
- return false;
- }
- }
- try {
- mActivityManager.setFocusedTask(win.getTask().mTaskId);
- } catch(RemoteException e) {}
- return true;
- }
-
- private void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
- int taskId = -1;
- synchronized (mWindowMap) {
- final Task task = displayContent.findTaskForResizePoint(x, y);
- if (task != null) {
- if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
- task.preserveOrientationOnResize(), x, y)) {
- return;
- }
- taskId = task.mTaskId;
- } else {
- taskId = displayContent.taskIdFromPoint(x, y);
- }
- }
- if (taskId >= 0) {
- try {
- mActivityManager.setFocusedTask(taskId);
- } catch(RemoteException e) {}
- }
- }
-
- private boolean startPositioningLocked(WindowState win, boolean resize,
- boolean preserveOrientation, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING)
- Slog.d(TAG_WM, "startPositioningLocked: "
- + "win=" + win + ", resize=" + resize + ", preserveOrientation="
- + preserveOrientation + ", {" + startX + ", " + startY + "}");
-
- if (win == null || win.getAppToken() == null) {
- Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
- return false;
- }
- if (win.mInputChannel == null) {
- Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
- + " probably being removed");
- return false;
- }
-
- final DisplayContent displayContent = win.getDisplayContent();
- if (displayContent == null) {
- Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
- return false;
- }
-
- Display display = displayContent.getDisplay();
- mTaskPositioner = new TaskPositioner(this);
- mTaskPositioner.register(displayContent);
- mInputMonitor.updateInputWindowsLw(true /*force*/);
-
- // We need to grab the touch focus so that the touch events during the
- // resizing/scrolling are not sent to the app. 'win' is the main window
- // of the app, it may not have focus since there might be other windows
- // on top (eg. a dialog window).
- WindowState transferFocusFromWin = win;
- if (mCurrentFocus != null && mCurrentFocus != win
- && mCurrentFocus.mAppToken == win.mAppToken) {
- transferFocusFromWin = mCurrentFocus;
- }
- if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
- Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- return false;
- }
-
- mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
- return true;
- }
-
- private void finishPositioning() {
- if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG_WM, "finishPositioning");
- }
- synchronized (mWindowMap) {
- if (mTaskPositioner != null) {
- mTaskPositioner.unregister();
- mTaskPositioner = null;
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- }
- }
- }
-
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
@@ -5003,12 +4906,13 @@
}
case TAP_OUTSIDE_TASK: {
- handleTapOutsideTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+ mTaskPositioningController.handleTapOutsideTask(
+ (DisplayContent)msg.obj, msg.arg1, msg.arg2);
}
break;
case FINISH_TASK_POSITIONING: {
- finishPositioning();
+ mTaskPositioningController.finishPositioning();
}
break;
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 44e27a5..8385020 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -41,11 +41,14 @@
static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
jint versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
std::string path;
+ const ProfileData* data = nullptr;
LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
- ScopedNullableByteArrayRO buffer(env, jdata);
- if (buffer.size() != -1) {
+ ScopedByteArrayRO buffer{env};
+ if (jdata != nullptr) {
+ buffer.reset(jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
- "Buffer size %zd doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
+ "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
+ data = reinterpret_cast<const ProfileData*>(buffer.get());
}
if (jpath != nullptr) {
ScopedUtfChars pathChars(env, jpath);
@@ -58,8 +61,7 @@
LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
const std::string package(packageChars.c_str(), packageChars.size());
- GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime,
- reinterpret_cast<const ProfileData*>(buffer.get()));
+ GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
}
static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
@@ -106,4 +108,4 @@
sMethods, NELEM(sMethods));
}
-} // namespace android
+} // namespace android
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b979ff4..91ba87e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -79,7 +79,6 @@
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
-import android.app.admin.IDevicePolicyManager;
import android.app.admin.NetworkEvent;
import android.app.admin.PasswordMetrics;
import android.app.admin.SecurityLog;
@@ -740,7 +739,7 @@
private static final String TAG_ORGANIZATION_NAME = "organization-name";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
- private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled";
+ private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
final DeviceAdminInfo info;
@@ -790,7 +789,7 @@
boolean requireAutoTime = false; // Can only be set by a device owner.
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
- boolean isLogoutButtonEnabled = false; // Can only be set by a device owner.
+ boolean isLogoutEnabled = false; // Can only be set by a device owner.
// one notification after enabling + one more after reboots
static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
@@ -1108,10 +1107,10 @@
out.text(organizationName);
out.endTag(null, TAG_ORGANIZATION_NAME);
}
- if (isLogoutButtonEnabled) {
- out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled));
- out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+ if (isLogoutEnabled) {
+ out.startTag(null, TAG_IS_LOGOUT_ENABLED);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
+ out.endTag(null, TAG_IS_LOGOUT_ENABLED);
}
}
@@ -1286,8 +1285,8 @@
} else {
Log.w(LOG_TAG, "Missing text when loading organization name");
}
- } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) {
- isLogoutButtonEnabled = Boolean.parseBoolean(
+ } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
+ isLogoutEnabled = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
@@ -11581,35 +11580,35 @@
}
@Override
- public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) {
+ public synchronized void setLogoutEnabled(ComponentName admin, boolean enabled) {
if (!mHasFeature) {
return;
}
Preconditions.checkNotNull(admin);
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (enabled == isLogoutButtonEnabledInternalLocked()) {
+ if (enabled == isLogoutEnabledInternalLocked()) {
// already in the requested state
return;
}
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- deviceOwner.isLogoutButtonEnabled = enabled;
+ deviceOwner.isLogoutEnabled = enabled;
saveSettingsLocked(mInjector.userHandleGetCallingUserId());
}
@Override
- public boolean isLogoutButtonEnabled() {
+ public boolean isLogoutEnabled() {
if (!mHasFeature) {
return false;
}
synchronized (this) {
- return isLogoutButtonEnabledInternalLocked();
+ return isLogoutEnabledInternalLocked();
}
}
- private boolean isLogoutButtonEnabledInternalLocked() {
+ private boolean isLogoutEnabledInternalLocked() {
ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
- return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled;
+ return (deviceOwner != null) && deviceOwner.isLogoutEnabled;
}
}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index b305b33..fdb366c 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -163,10 +163,10 @@
// TODO: Find an lighter weight approach.
private class LoggingCallbackWrapper extends Callback {
private static final String PREFIX = "INVOKE ";
- private final Callback mCallback;
+ private Callback mCallback;
public LoggingCallbackWrapper(Callback callback) {
- mCallback = (callback != null) ? callback : new Callback();
+ mCallback = callback;
}
private void log(String msg) {
@@ -1283,7 +1283,6 @@
stopAllIP();
resetLinkProperties();
- mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
if (mStartTimeMillis > 0) {
recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
mStartTimeMillis = 0;
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 0ea8d4f..5d739a3 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -32,6 +32,9 @@
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -57,7 +60,7 @@
private ConfigHelper mMockConfigHelper;
private PackageManagerHelper mMockPackageManagerHelper;
- private FakeClockHelper mFakeClock;
+ private FakeClock mFakeClock;
private FakeIntentHelper mFakeIntentHelper;
private PackageStatusStorage mPackageStatusStorage;
private PackageTracker mPackageTracker;
@@ -66,7 +69,7 @@
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getContext();
- mFakeClock = new FakeClockHelper();
+ mFakeClock = new FakeClock();
// Read-only interfaces so are easy to mock.
mMockConfigHelper = mock(ConfigHelper.class);
@@ -1444,18 +1447,33 @@
}
}
- private static class FakeClockHelper implements ClockHelper {
+ private static class FakeClock extends Clock {
private long currentTime = 1000;
@Override
- public long currentTimestamp() {
+ public long millis() {
return currentTime;
}
public void incrementClock(long millis) {
currentTime += millis;
}
+
+ @Override
+ public ZoneId getZone() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Clock withZone(ZoneId zone) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Instant instant() {
+ throw new UnsupportedOperationException();
+ }
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 87b34ab..86ce90c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,6 +18,7 @@
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -364,4 +365,29 @@
reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
}
+
+ @Test
+ public void testPredictionTimedout() throws Exception {
+ AppStandbyController controller = setupController();
+ setChargingState(controller, false);
+ controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_PREDICTED + "CTS", 1 * HOUR_MS);
+
+ // Fast forward 12 hours
+ mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
+ controller.checkIdleStates(USER_ID);
+ // Should still be in predicted bucket, since prediction timeout is 1 day since prediction
+ assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+ // Fast forward two more hours
+ mInjector.mElapsedRealtime += 2 * HOUR_MS;
+ controller.checkIdleStates(USER_ID);
+ // Should have now applied prediction timeout
+ assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+
+ // Fast forward RARE bucket
+ mInjector.mElapsedRealtime += RARE_THRESHOLD;
+ controller.checkIdleStates(USER_ID);
+ // Should continue to apply prediction timeout
+ assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
new file mode 100644
index 0000000..9ecf51e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.server.wm;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Choreographer;
+import android.view.Choreographer.FrameCallback;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+
+import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Test class for {@link SurfaceAnimationRunner}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.SurfaceAnimationRunnerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SurfaceAnimationRunnerTest extends WindowTestsBase {
+
+ @Mock SurfaceControl mMockSurface;
+ @Mock Transaction mMockTransaction;
+ @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private SurfaceAnimationRunner mSurfaceAnimationRunner;
+ private CountDownLatch mFinishCallbackLatch;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ mFinishCallbackLatch = new CountDownLatch(1);
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */,
+ mMockTransaction);
+ }
+
+ private void finishedCallback() {
+ mFinishCallbackLatch.countDown();
+ }
+
+ @Test
+ public void testAnimation() throws Exception {
+ mSurfaceAnimationRunner
+ .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
+ this::finishedCallback);
+
+ // Ensure that the initial transformation has been applied.
+ final Matrix m = new Matrix();
+ m.setTranslate(-10, 0);
+ verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
+ verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f));
+
+ mFinishCallbackLatch.await(1, SECONDS);
+ assertFinishCallbackCalled();
+
+ m.setTranslate(10, 0);
+ verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
+
+ // At least 3 times: After initialization, first frame, last frame.
+ verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f));
+ }
+
+ @Test
+ public void testCancel_notStarted() throws Exception {
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+ mSurfaceAnimationRunner
+ .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
+ this::finishedCallback);
+ mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
+ waitUntilHandlersIdle();
+ assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty());
+ assertFinishCallbackNotCalled();
+ //verify(mMockSurface).release();
+ }
+
+ @Test
+ public void testCancel_running() throws Exception {
+ mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), mMockTransaction);
+ mSurfaceAnimationRunner
+ .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
+ this::finishedCallback);
+ waitUntilNextFrame();
+ assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
+ mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface);
+ assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
+ waitUntilHandlersIdle();
+ assertFinishCallbackNotCalled();
+ //verify(mMockSurface).release();
+ }
+
+ private void waitUntilNextFrame() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
+ latch::countDown, null /* token */);
+ latch.await();
+ }
+
+ private void assertFinishCallbackCalled() {
+ assertEquals(0, mFinishCallbackLatch.getCount());
+ }
+
+ private void assertFinishCallbackNotCalled() {
+ assertEquals(1, mFinishCallbackLatch.getCount());
+ }
+
+ private AnimationSpec createTranslateAnimation() {
+ final Animation a = new TranslateAnimation(-10, 10, 0, 0);
+ a.initialize(0, 0, 0, 0);
+ a.setDuration(50);
+ return new WindowAnimationSpec(a, new Point(0, 0));
+ }
+
+ /**
+ * Callback provider that doesn't animate at all.
+ */
+ private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider {
+
+ @Override
+ public void postFrameCallback(FrameCallback callback) {
+ }
+
+ @Override
+ public void postCommitCallback(Runnable runnable) {
+ }
+
+ @Override
+ public long getFrameTime() {
+ return 0;
+ }
+
+ @Override
+ public long getFrameDelay() {
+ return 0;
+ }
+
+ @Override
+ public void setFrameDelay(long delay) {
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
new file mode 100644
index 0000000..9a52042
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.server.wm;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Builder;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceSession;
+
+import com.google.android.collect.Lists;
+
+import com.android.server.wm.SurfaceAnimator.Animatable;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/**
+ * Test class for {@link SurfaceAnimatorTest}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.SurfaceAnimatorTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SurfaceAnimatorTest extends WindowTestsBase {
+
+ @Mock
+ AnimationAdapter mSpec;
+ @Mock
+ AnimationAdapter mSpec2;
+ @Mock Transaction mTransaction;
+
+ private SurfaceAnimator mSurfaceAnimator;
+ private SurfaceControl mParent;
+ private SurfaceControl mSurface;
+ private boolean mFinishedCallbackCalled;
+ private SurfaceControl mLeash;
+ private SurfaceSession mSession = new SurfaceSession();
+
+ private final Animatable mAnimatable = new Animatable() {
+ @Override
+ public Transaction getPendingTransaction() {
+ return mTransaction;
+ }
+
+ @Override
+ public void commitPendingTransaction() {
+ }
+
+ @Override
+ public void onLeashCreated(Transaction t, SurfaceControl leash) {
+ }
+
+ @Override
+ public void onLeashDestroyed(Transaction t) {
+ }
+
+ @Override
+ public Builder makeLeash() {
+ return new SurfaceControl.Builder(mSession) {
+
+ @Override
+ public SurfaceControl build() {
+ mLeash = super.build();
+ return mLeash;
+ }
+ }.setParent(mParent);
+ }
+
+ @Override
+ public SurfaceControl getSurface() {
+ return mSurface;
+ }
+
+ @Override
+ public SurfaceControl getParentSurface() {
+ return mParent;
+ }
+
+ @Override
+ public int getSurfaceWidth() {
+ return 1;
+ }
+
+ @Override
+ public int getSurfaceHeight() {
+ return 1;
+ }
+ };
+
+ private final Runnable mFinishedCallback = () -> {
+ mFinishedCallbackCalled = true;
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mParent = sWm.makeSurfaceBuilder(mSession)
+ .setName("test surface parent")
+ .setSize(3000, 3000)
+ .build();
+ mSurface = sWm.makeSurfaceBuilder(mSession)
+ .setName("test surface")
+ .setSize(1, 1)
+ .build();
+ mFinishedCallbackCalled = false;
+ mLeash = null;
+ mSurfaceAnimator = new SurfaceAnimator(mAnimatable, mFinishedCallback, sWm);
+ }
+
+ @Test
+ public void testRunAnimation() throws Exception {
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+ OnAnimationFinishedCallback.class);
+
+ assertTrue(mSurfaceAnimator.isAnimating());
+ assertNotNull(mSurfaceAnimator.getAnimation());
+ verify(mTransaction).reparent(eq(mSurface), eq(mLeash.getHandle()));
+ verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+ callbackCaptor.getValue().onAnimationFinished(mSpec);
+ assertFalse(mSurfaceAnimator.isAnimating());
+ assertNull(mSurfaceAnimator.getAnimation());
+ assertTrue(mFinishedCallbackCalled);
+
+ // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
+ }
+
+ @Test
+ public void testOverrideAnimation() throws Exception {
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
+
+ assertFalse(mFinishedCallbackCalled);
+
+ final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
+ OnAnimationFinishedCallback.class);
+ assertTrue(mSurfaceAnimator.isAnimating());
+ assertNotNull(mSurfaceAnimator.getAnimation());
+ verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+ // First animation was finished, but this shouldn't cancel the second animation
+ callbackCaptor.getValue().onAnimationFinished(mSpec);
+ assertTrue(mSurfaceAnimator.isAnimating());
+
+ // Second animation was finished
+ verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture());
+ callbackCaptor.getValue().onAnimationFinished(mSpec2);
+ assertFalse(mSurfaceAnimator.isAnimating());
+ assertTrue(mFinishedCallbackCalled);
+ }
+
+ @Test
+ public void testCancelAnimation() throws Exception {
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ assertTrue(mSurfaceAnimator.isAnimating());
+ mSurfaceAnimator.cancelAnimation();
+ assertFalse(mSurfaceAnimator.isAnimating());
+ verify(mSpec).onAnimationCancelled(any());
+ assertTrue(mFinishedCallbackCalled);
+ }
+
+ @Test
+ public void testDelayingAnimationStart() throws Exception {
+ mSurfaceAnimator.startDelayingAnimationStart();
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verifyZeroInteractions(mSpec);
+ assertTrue(mSurfaceAnimator.isAnimating());
+ mSurfaceAnimator.endDelayingAnimationStart();
+ verify(mSpec).startAnimation(any(), any(), any());
+ }
+
+ @Test
+ public void testDelayingAnimationStartAndCancelled() throws Exception {
+ mSurfaceAnimator.startDelayingAnimationStart();
+ mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ mSurfaceAnimator.cancelAnimation();
+ verifyZeroInteractions(mSpec);
+ assertFalse(mSurfaceAnimator.isAnimating());
+ assertTrue(mFinishedCallbackCalled);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
new file mode 100644
index 0000000..89447a9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -0,0 +1,115 @@
+/*
+ * 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.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.InputChannel;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+/**
+ * Tests for the {@link TaskPositioningController} class.
+ *
+ * atest com.android.server.wm.TaskPositioningControllerTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class TaskPositioningControllerTests extends WindowTestsBase {
+ private static final int TIMEOUT_MS = 1000;
+ private TaskPositioningController mTarget;
+ private WindowState mWindow;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ assertNotNull(sWm.mTaskPositioningController);
+ mTarget = sWm.mTaskPositioningController;
+
+ when(sWm.mInputManager.transferTouchFocus(
+ any(InputChannel.class),
+ any(InputChannel.class))).thenReturn(true);
+
+ mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ mWindow.mInputChannel = new InputChannel();
+ synchronized (sWm.mWindowMap) {
+ sWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
+ }
+ }
+
+ @Test
+ public void testStartAndFinishPositioning() throws Exception {
+ synchronized (sWm.mWindowMap) {
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+ }
+
+ assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0));
+
+ synchronized (sWm.mWindowMap) {
+ assertTrue(mTarget.isPositioningLocked());
+ assertNotNull(mTarget.getDragWindowHandleLocked());
+ }
+
+ assertTrue(sWm.mH.runWithScissors(() -> {
+ mTarget.finishPositioning();
+ }, TIMEOUT_MS));
+
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+ }
+
+ @Test
+ public void testHandleTapOutsideTask() throws Exception {
+ synchronized (sWm.mWindowMap) {
+
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+ }
+
+ final DisplayContent content = mock(DisplayContent.class);
+ when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask());
+ assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
+
+ mTarget.handleTapOutsideTask(content, 0, 0);
+
+ synchronized (sWm.mWindowMap) {
+ assertTrue(mTarget.isPositioningLocked());
+ assertNotNull(mTarget.getDragWindowHandleLocked());
+ }
+
+ assertTrue(sWm.mH.runWithScissors(() -> {
+ mTarget.finishPositioning();
+ }, TIMEOUT_MS));
+
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 4c5e291..c699a94 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -185,6 +185,7 @@
void waitUntilHandlersIdle() {
sWm.mH.runWithScissors(() -> { }, 0);
sWm.mAnimationHandler.runWithScissors(() -> { }, 0);
+ SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
}
private WindowToken createWindowToken(
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 5aef55b..6ac4b36 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -72,14 +72,13 @@
private static final String ATTR_SCREEN_IDLE = "screenIdleTime";
// Elapsed timebase time when app was last used
private static final String ATTR_ELAPSED_IDLE = "elapsedIdleTime";
+ // Elapsed timebase time when the app bucket was last predicted externally
+ private static final String ATTR_LAST_PREDICTED_TIME = "lastPredictedTime";
+ // The standby bucket for the app
private static final String ATTR_CURRENT_BUCKET = "appLimitBucket";
+ // The reason the app was put in the above bucket
private static final String ATTR_BUCKETING_REASON = "bucketReason";
- // State that was last informed to listeners, since boot
- private static final int STATE_UNINFORMED = 0;
- private static final int STATE_ACTIVE = 1;
- private static final int STATE_IDLE = 2;
-
// device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
private long mElapsedDuration; // Total device on duration since device was "born"
@@ -92,13 +91,21 @@
private boolean mScreenOn;
- private static class AppUsageHistory {
+ static class AppUsageHistory {
+ // Debug
final byte[] recent = new byte[HISTORY_SIZE];
+ // Last used time using elapsed timebase
long lastUsedElapsedTime;
+ // Last used time using screen_on timebase
long lastUsedScreenTime;
+ // Last predicted time using elapsed timebase
+ long lastPredictedTime;
+ // Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
+ // Reason for setting the standby bucket. TODO: Switch to int.
String bucketingReason;
+ // In-memory only, last bucket for which the listeners were informed
int lastInformedBucket;
}
@@ -269,6 +276,7 @@
appUsageHistory = new AppUsageHistory();
appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
+ appUsageHistory.lastPredictedTime = getElapsedTime(0);
appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
@@ -295,6 +303,14 @@
}
}
+ public AppUsageHistory getAppUsageHistory(String packageName, int userId,
+ long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+ return appUsageHistory;
+ }
+
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
int bucket, String reason) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
@@ -302,6 +318,9 @@
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
+ if (reason.startsWith(UsageStatsManager.REASON_PREDICTED)) {
+ appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
+ }
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
+ ", reason=" + appUsageHistory.bucketingReason);
@@ -322,7 +341,7 @@
return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
}
- private long getElapsedTime(long elapsedRealtime) {
+ public long getElapsedTime(long elapsedRealtime) {
return (elapsedRealtime - mElapsedSnapshot + mElapsedDuration);
}
@@ -431,6 +450,12 @@
Long.parseLong(parser.getAttributeValue(null, ATTR_ELAPSED_IDLE));
appUsageHistory.lastUsedScreenTime =
Long.parseLong(parser.getAttributeValue(null, ATTR_SCREEN_IDLE));
+ String lastPredictedTimeString = parser.getAttributeValue(null,
+ ATTR_LAST_PREDICTED_TIME);
+ if (lastPredictedTimeString != null) {
+ appUsageHistory.lastPredictedTime =
+ Long.parseLong(lastPredictedTimeString);
+ }
String currentBucketString = parser.getAttributeValue(null,
ATTR_CURRENT_BUCKET);
appUsageHistory.currentBucket = currentBucketString == null
@@ -441,6 +466,7 @@
if (appUsageHistory.bucketingReason == null) {
appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
}
+ appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
}
}
@@ -477,6 +503,8 @@
Long.toString(history.lastUsedElapsedTime));
xml.attribute(null, ATTR_SCREEN_IDLE,
Long.toString(history.lastUsedScreenTime));
+ xml.attribute(null, ATTR_LAST_PREDICTED_TIME,
+ Long.toString(history.lastPredictedTime));
xml.attribute(null, ATTR_CURRENT_BUCKET,
Integer.toString(history.currentBucket));
xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
@@ -512,6 +540,8 @@
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
idpw.print(" lastUsedScreenOn=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
+ idpw.print(" lastPredictedTime=");
+ TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
idpw.print(" bucket=" + appUsageHistory.currentBucket
+ " reason=" + appUsageHistory.bucketingReason);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 3d20a64..d8086bb 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -18,6 +18,7 @@
import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
import static android.app.usage.UsageStatsManager.REASON_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
@@ -118,6 +119,9 @@
STANDBY_BUCKET_RARE
};
+ // Expiration time for predicted bucket
+ private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+
// To name the lock for stack traces
static class Lock {}
@@ -388,25 +392,27 @@
STANDBY_BUCKET_EXEMPTED);
} else {
synchronized (mAppIdleLock) {
- String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
+ AppIdleHistory.AppUsageHistory app =
+ mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
// If the bucket was forced by the developer, leave it alone
- if (REASON_FORCED.equals(bucketingReason)) {
+ if (REASON_FORCED.equals(app.bucketingReason)) {
continue;
}
+ boolean predictionLate = false;
// If the bucket was moved up due to usage, let the timeouts apply.
- if (REASON_DEFAULT.equals(bucketingReason)
- || REASON_USAGE.equals(bucketingReason)
- || REASON_TIMEOUT.equals(bucketingReason)) {
- int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
- elapsedRealtime);
+ if (REASON_DEFAULT.equals(app.bucketingReason)
+ || REASON_USAGE.equals(app.bucketingReason)
+ || REASON_TIMEOUT.equals(app.bucketingReason)
+ || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
+ int oldBucket = app.currentBucket;
int newBucket = getBucketForLocked(packageName, userId,
elapsedRealtime);
if (DEBUG) {
Slog.d(TAG, " Old bucket=" + oldBucket
+ ", newBucket=" + newBucket);
}
- if (oldBucket < newBucket) {
+ if (oldBucket < newBucket || predictionLate) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, REASON_TIMEOUT);
maybeInformListeners(packageName, userId, elapsedRealtime,
@@ -424,6 +430,14 @@
return true;
}
+ private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
+ return app.bucketingReason != null
+ && app.bucketingReason.startsWith(REASON_PREDICTED)
+ && app.lastPredictedTime > 0
+ && mAppIdleHistory.getElapsedTime(elapsedRealtime)
+ - app.lastPredictedTime > PREDICTION_TIMEOUT;
+ }
+
private void maybeInformListeners(String packageName, int userId,
long elapsedRealtime, int bucket) {
synchronized (mAppIdleLock) {
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index a5d67c6..dd2a6df 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -83,7 +83,10 @@
public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
/** @hide */
- @StringDef({FORMAT_3GPP, FORMAT_3GPP2})
+ @StringDef(prefix = { "FORMAT_" }, value = {
+ FORMAT_3GPP,
+ FORMAT_3GPP2
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface Format {}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 5a4db99..1670e6b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -16,15 +16,23 @@
package android.telephony.ims.stub;
+import android.content.Context;
+import android.content.Intent;
import android.os.RemoteException;
+import android.util.Log;
import com.android.ims.ImsConfig;
import com.android.ims.ImsConfigListener;
import com.android.ims.internal.IImsConfig;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+
/**
- * Base implementation of ImsConfig, which implements stub versions of the methods
- * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ * Base implementation of ImsConfig.
+ * Override the methods that your implementation of ImsConfig supports.
*
* DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
* will break other implementations of ImsConfig maintained by other ImsServices.
@@ -34,10 +42,25 @@
* 1) Items provisioned by the operator.
* 2) Items configured by user. Mainly service feature class.
*
+ * The inner class {@link ImsConfigStub} implements methods of IImsConfig AIDL interface.
+ * The IImsConfig AIDL interface is called by ImsConfig, which may exist in many other processes.
+ * ImsConfigImpl access to the configuration parameters may be arbitrarily slow, especially in
+ * during initialization, or times when a lot of configuration parameters are being set/get
+ * (such as during boot up or SIM card change). By providing a cache in ImsConfigStub, we can speed
+ * up access to these configuration parameters, so a query to the ImsConfigImpl does not have to be
+ * performed every time.
* @hide
*/
-public class ImsConfigImplBase extends IImsConfig.Stub {
+public class ImsConfigImplBase {
+
+ static final private String TAG = "ImsConfigImplBase";
+
+ ImsConfigStub mImsConfigStub;
+
+ public ImsConfigImplBase(Context context) {
+ mImsConfigStub = new ImsConfigStub(this, context);
+ }
/**
* Gets the value for ims service/capabilities parameters from the provisioned
@@ -46,7 +69,6 @@
* @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
* @return value in Integer format.
*/
- @Override
public int getProvisionedValue(int item) throws RemoteException {
return -1;
}
@@ -58,7 +80,6 @@
* @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
* @return value in String format.
*/
- @Override
public String getProvisionedStringValue(int item) throws RemoteException {
return null;
}
@@ -72,7 +93,6 @@
* @param value in Integer format.
* @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
*/
- @Override
public int setProvisionedValue(int item, int value) throws RemoteException {
return ImsConfig.OperationStatusConstants.FAILED;
}
@@ -86,7 +106,6 @@
* @param value in String format.
* @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
*/
- @Override
public int setProvisionedStringValue(int item, String value) throws RemoteException {
return ImsConfig.OperationStatusConstants.FAILED;
}
@@ -100,7 +119,6 @@
* @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
* @param listener feature value returned asynchronously through listener.
*/
- @Override
public void getFeatureValue(int feature, int network, ImsConfigListener listener)
throws RemoteException {
}
@@ -115,7 +133,6 @@
* @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
* @param listener, provided if caller needs to be notified for set result.
*/
- @Override
public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
throws RemoteException {
}
@@ -124,7 +141,6 @@
* Gets the value for IMS VoLTE provisioned.
* This should be the same as the operator provisioned value if applies.
*/
- @Override
public boolean getVolteProvisioned() throws RemoteException {
return false;
}
@@ -134,7 +150,6 @@
*
* @param listener Video quality value returned asynchronously through listener.
*/
- @Override
public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
}
@@ -144,7 +159,233 @@
* @param quality, defines the value of video quality.
* @param listener, provided if caller needs to be notified for set result.
*/
- @Override
public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
}
+
+ public IImsConfig getIImsConfig() { return mImsConfigStub; }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ */
+ public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Updates provisioning value and notifies the framework of the change.
+ * Doesn't call #setProvisionedValue and assumes the result succeeded.
+ * This should only be used by modem when they implicitly changed provisioned values.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ */
+ public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value, true);
+ }
+
+ /**
+ * Implements the IImsConfig AIDL interface, which is called by potentially many processes
+ * in order to get/set configuration parameters.
+ *
+ * It holds an object of ImsConfigImplBase class which is usually extended by ImsConfigImpl
+ * with actual implementations from vendors. This class caches provisioned values from
+ * ImsConfigImpl layer because queries through ImsConfigImpl can be slow. When query goes in,
+ * it first checks cache layer. If missed, it will call the vendor implementation of
+ * ImsConfigImplBase API.
+ * and cache the return value if the set succeeds.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ static public class ImsConfigStub extends IImsConfig.Stub {
+ Context mContext;
+ WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
+ private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
+ private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+
+ @VisibleForTesting
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Context context) {
+ mContext = context;
+ mImsConfigImplBaseWeakReference =
+ new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public synchronized int getProvisionedValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ int retVal = getImsConfigImpl().getProvisionedValue(item);
+ if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters. It first checks its local cache,
+ * if missed, it will call #ImsConfigImplBase.getProvisionedValue.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public synchronized String getProvisionedStringValue(int item) throws RemoteException {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedStringValue.get(item);
+ } else {
+ String retVal = getImsConfigImpl().getProvisionedStringValue(item);
+ if (retVal != null) {
+ updateCachedValue(item, retVal, false);
+ }
+ return retVal;
+ }
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedValue(int item, int value) throws RemoteException {
+ mProvisionedIntValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, retVal, true);
+ } else {
+ Log.d(TAG, "Set provision value of " + item +
+ " to " + value + " failed with error code " + retVal);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived, and write it into local cache.
+ * Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public synchronized int setProvisionedStringValue(int item, String value)
+ throws RemoteException {
+ mProvisionedStringValue.remove(item);
+ int retVal = getImsConfigImpl().setProvisionedStringValue(item, value);
+ if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ updateCachedValue(item, retVal, true);
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getFeatureValue.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().getFeatureValue(feature, network, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setFeatureValue.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setFeatureValue(feature, network, value, listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVolteProvisioned.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return getImsConfigImpl().getVolteProvisioned();
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.getVideoQuality.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ getImsConfigImpl().getVideoQuality(listener);
+ }
+
+ /**
+ * Wrapper function to call ImsConfigImplBase.setVideoQuality.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener)
+ throws RemoteException {
+ getImsConfigImpl().setVideoQuality(quality, listener);
+ }
+
+ private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
+ ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get();
+ if (ref == null) {
+ throw new RemoteException("Fail to get ImsConfigImpl");
+ } else {
+ return ref;
+ }
+ }
+
+ private void sendImsConfigChangedIntent(int item, int value) {
+ sendImsConfigChangedIntent(item, Integer.toString(value));
+ }
+
+ private void sendImsConfigChangedIntent(int item, String value) {
+ Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
+ configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
+ if (mContext != null) {
+ mContext.sendBroadcast(configChangedIntent);
+ }
+ }
+
+ protected synchronized void updateCachedValue(int item, int value, boolean notifyChange) {
+ mProvisionedIntValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+
+ protected synchronized void updateCachedValue(
+ int item, String value, boolean notifyChange) {
+ mProvisionedStringValue.put(item, value);
+ if (notifyChange) {
+ sendImsConfigChangedIntent(item, value);
+ }
+ }
+ }
}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index cf4c47b..cd0c4b1 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -619,6 +619,7 @@
Rlog.d(TAG, "setProvisionedValue(): item = " + item +
" value = " + value + " ret = " + ret);
}
+
return ret;
}
@@ -647,6 +648,7 @@
Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
", value =" + value);
}
+
return ret;
}
diff --git a/test-base/Android.bp b/test-base/Android.bp
new file mode 100644
index 0000000..a3fd345
--- /dev/null
+++ b/test-base/Android.bp
@@ -0,0 +1,88 @@
+//
+// 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.
+//
+
+// Build the android.test.base library
+// ===================================
+// This contains the junit.framework and android.test classes that were in
+// Android API level 25 excluding those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library {
+ name: "android.test.base",
+
+ srcs: ["src/**/*.java"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ ],
+
+}
+
+// Build the legacy-test library
+// =============================
+// This contains the junit.framework and android.test classes that were in
+// Android API level 25 excluding those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library {
+ name: "legacy-test",
+ static_libs: ["android.test.base"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ ],
+}
+
+// Build the repackaged.android.test.base library
+// ==============================================
+// This contains repackaged versions of the classes from legacy-test.
+java_library_static {
+ name: "repackaged.android.test.base",
+
+ static_libs: ["android.test.base"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ ],
+
+ jarjar_rules: "jarjar-rules.txt",
+}
+
+// Build the legacy-android-test library
+// =====================================
+// This contains the android.test classes that were in Android API level 25,
+// including those from android.test.runner.
+// Also contains the com.android.internal.util.Predicate[s] classes.
+java_library_static {
+ name: "legacy-android-test",
+
+ srcs: [
+ "src/android/**/*.java",
+ "src/com/**/*.java",
+ ],
+
+ static_libs: [
+ "android.test.runner",
+ "android.test.mock",
+ ],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "junit",
+ ],
+}
diff --git a/test-base/Android.mk b/test-base/Android.mk
index 03bdcf23..5e5d040 100644
--- a/test-base/Android.mk
+++ b/test-base/Android.mk
@@ -16,50 +16,6 @@
LOCAL_PATH:= $(call my-dir)
-# Build the android.test.base library
-# ===================================
-# This contains the junit.framework and android.test classes that were in
-# Android API level 25 excluding those from android.test.runner.
-# Also contains the com.android.internal.util.Predicate[s] classes.
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := android.test.base
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-include $(BUILD_JAVA_LIBRARY)
-
-# Build the legacy-test library
-# =============================
-# This contains the junit.framework and android.test classes that were in
-# Android API level 25 excluding those from android.test.runner.
-# Also contains the com.android.internal.util.Predicate[s] classes.
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := legacy-test
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-include $(BUILD_JAVA_LIBRARY)
-
-# Build the repackaged.android.test.base library
-# ==============================================
-# This contains repackaged versions of the classes from legacy-test.
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := repackaged.android.test.base
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
@@ -156,24 +112,6 @@
endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
-# Build the legacy-android-test library
-# =====================================
-# This contains the android.test classes that were in Android API level 25,
-# including those from android.test.runner.
-# Also contains the com.android.internal.util.Predicate[s] classes.
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src/android) \
- $(call all-java-files-under, ../test-runner/src/android) \
- $(call all-java-files-under, ../test-mock/src/android) \
- $(call all-java-files-under, src/com)
-LOCAL_MODULE := legacy-android-test
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework junit
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Build the legacy.test.stubs library
# ===================================
include $(CLEAR_VARS)
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
new file mode 100644
index 0000000..8eddec4
--- /dev/null
+++ b/test-mock/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2008 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.
+//
+
+// Build the android.test.mock library
+// ===================================
+java_library {
+ name: "android.test.mock",
+
+ srcs: ["src/**/*.java"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "legacy-test",
+ ],
+}
+
+// Build the repackaged.android.test.mock library
+// ==============================================
+java_library_static {
+ name: "repackaged.android.test.mock",
+
+ static_libs: ["android.test.mock"],
+
+ jarjar_rules: "jarjar-rules.txt",
+}
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index 2c07955..a761a07 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -18,32 +18,6 @@
android_test_mock_source_files := $(call all-java-files-under, src/android/test/mock)
-# Build the repackaged.android.test.mock library
-# ==============================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt
-
-LOCAL_MODULE:= repackaged.android.test.mock
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the android.test.mock library
-# ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework
-
-LOCAL_MODULE:= android.test.mock
-
-include $(BUILD_JAVA_LIBRARY)
-
# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
new file mode 120000
index 0000000..f6f7913
--- /dev/null
+++ b/test-mock/jarjar-rules.txt
@@ -0,0 +1 @@
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
new file mode 100644
index 0000000..104ae82
--- /dev/null
+++ b/test-runner/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2008 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.
+//
+
+// Build the android.test.runner library
+// =====================================
+java_library {
+ name: "android.test.runner",
+
+ srcs: ["src/**/*.java"],
+
+ no_framework_libs: true,
+ libs: [
+ "framework",
+ "legacy-test",
+ "android.test.mock",
+ ],
+}
+
+// Build the repackaged.android.test.runner library
+// ================================================
+java_library_static {
+ name: "repackaged.android.test.runner",
+
+ static_libs: ["android.test.runner"],
+
+ jarjar_rules: "jarjar-rules.txt",
+}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 87fe831..67f1354 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -16,42 +16,6 @@
LOCAL_PATH:= $(call my-dir)
-# Build the android.test.runner library
-# =====================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- core-oj \
- core-libart \
- framework \
- android.test.base \
- android.test.mock \
-
-LOCAL_MODULE:= android.test.runner
-
-include $(BUILD_JAVA_LIBRARY)
-
-# Build the repackaged.android.test.runner library
-# ================================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := \
- core-oj \
- core-libart \
- framework \
- android.test.base \
- android.test.mock \
-
-LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt
-
-LOCAL_MODULE:= repackaged.android.test.runner
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
diff --git a/test-runner/jarjar-rules.txt b/test-runner/jarjar-rules.txt
new file mode 120000
index 0000000..f6f7913
--- /dev/null
+++ b/test-runner/jarjar-rules.txt
@@ -0,0 +1 @@
+../test-base/jarjar-rules.txt
\ No newline at end of file
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
index ebf121a..22d88fb 100644
--- a/tests/net/java/android/net/ip/IpManagerTest.java
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -69,8 +69,6 @@
/**
* Tests for IpManager.
- *
- * TODO: Rename to IpClientTest.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -113,10 +111,6 @@
verify(mNMService, times(1)).registerObserver(arg.capture());
mObserver = arg.getValue();
reset(mNMService);
- final LinkProperties emptyLp = new LinkProperties();
- emptyLp.setInterfaceName(ifname);
- verify(mCb, timeout(100)).onLinkPropertiesChange(eq(emptyLp));
- reset(mCb);
return ipm;
}
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 33b5a8b..20a9f41 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -57,31 +57,30 @@
std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
+ std::unique_ptr<ResourceTable> table;
+
io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
- if (table_file == nullptr) {
- diag->Error(DiagMessage(source) << "failed to find " << kProtoResourceTablePath);
- return {};
- }
+ if (table_file != nullptr) {
+ pb::ResourceTable pb_table;
+ std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
+ if (in == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
+ return {};
+ }
- std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
- if (in == nullptr) {
- diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
- return {};
- }
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
+ diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
+ return {};
+ }
- pb::ResourceTable pb_table;
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
- diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
- return {};
- }
-
- std::string error;
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
- diag->Error(DiagMessage(source)
- << "failed to deserialize " << kProtoResourceTablePath << ": " << error);
- return {};
+ std::string error;
+ table = util::make_unique<ResourceTable>();
+ if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
+ diag->Error(DiagMessage(source)
+ << "failed to deserialize " << kProtoResourceTablePath << ": " << error);
+ return {};
+ }
}
io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
@@ -103,6 +102,7 @@
return {};
}
+ std::string error;
std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error);
if (manifest == nullptr) {
diag->Error(DiagMessage(source)
@@ -115,24 +115,21 @@
std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection(
const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
+ std::unique_ptr<ResourceTable> table;
+
io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
- if (table_file == nullptr) {
- diag->Error(DiagMessage(source) << "failed to find " << kApkResourceTablePath);
-
- return {};
- }
-
- std::unique_ptr<io::IData> data = table_file->OpenAsData();
- if (data == nullptr) {
- diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
- return {};
- }
-
- std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
- BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
- collection.get());
- if (!parser.Parse()) {
- return {};
+ if (table_file != nullptr) {
+ table = util::make_unique<ResourceTable>();
+ std::unique_ptr<io::IData> data = table_file->OpenAsData();
+ if (data == nullptr) {
+ diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
+ return {};
+ }
+ BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
+ collection.get());
+ if (!parser.Parse()) {
+ return {};
+ }
}
io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 2bd2405..964dacf 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -64,37 +64,39 @@
return false;
}
- // Resource table
- if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to serialize the resource table");
- return false;
+ if (apk->GetResourceTable() != nullptr) {
+ // Resource table
+ if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize the resource table");
+ return false;
+ }
+
+ // Resources
+ for (const auto& package : apk->GetResourceTable()->packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ for (const auto& config_value : entry->values) {
+ const FileReference* file = ValueCast<FileReference>(config_value->value.get());
+ if (file != nullptr) {
+ if (file->file == nullptr) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "no file associated with " << *file);
+ return false;
+ }
+
+ if (!serializer->SerializeFile(file, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize file " << *file->path);
+ return false;
+ }
+ } // file
+ } // config_value
+ } // entry
+ } // type
+ } // package
}
- // Resources
- for (const auto& package : apk->GetResourceTable()->packages) {
- for (const auto& type : package->types) {
- for (const auto& entry : type->entries) {
- for (const auto& config_value : entry->values) {
- const FileReference* file = ValueCast<FileReference>(config_value->value.get());
- if (file != nullptr) {
- if (file->file == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "no file associated with " << *file);
- return false;
- }
-
- if (!serializer->SerializeFile(file, writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to serialize file " << *file->path);
- return false;
- }
- } // file
- } // config_value
- } // entry
- } // type
- } // package
-
// Other files
std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
while (iterator->HasNext()) {
diff --git a/wifi/java/android/net/wifi/rtt/WifiRttManager.java b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
index 735e872..b4c690f 100644
--- a/wifi/java/android/net/wifi/rtt/WifiRttManager.java
+++ b/wifi/java/android/net/wifi/rtt/WifiRttManager.java
@@ -41,7 +41,7 @@
*
* @hide RTT_API
*/
-@SystemService(Context.WIFI_RTT2_SERVICE)
+@SystemService(Context.WIFI_RTT_RANGING_SERVICE)
public class WifiRttManager {
private static final String TAG = "WifiRttManager";
private static final boolean VDBG = false;
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
index 221b94b..a0d407a 100644
--- a/wifi/java/android/net/wifi/rtt/package.html
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -5,7 +5,7 @@
<p>The primary entry point to Wi-Fi RTT capabilities is the
{@link android.net.wifi.rtt.WifiRttManager} class, which is acquired by calling
{@link android.content.Context#getSystemService(String)
- Context.getSystemService(Context.WIFI_RTT_SERVICE)}</p>
+ Context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE)}</p>
<p>Some APIs may require the following user permissions:</p>
<ul>