Merge "Verify that b/68859852 won't happen again"
diff --git a/Android.mk b/Android.mk
index 7a3c46d..06f64f1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -219,6 +219,8 @@
core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \
core/java/android/hardware/location/IContextHubCallback.aidl \
+ core/java/android/hardware/location/IContextHubClient.aidl \
+ core/java/android/hardware/location/IContextHubClientCallback.aidl \
core/java/android/hardware/location/IContextHubService.aidl \
core/java/android/hardware/location/IContextHubTransactionCallback.aidl \
core/java/android/hardware/radio/IRadioService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 00a9452..2f3fc26 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -343,6 +343,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
+ field public static final int buttonCornerRadius = 16844149; // 0x1010575
field public static final int buttonGravity = 16844030; // 0x10104fe
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
@@ -47905,6 +47906,7 @@
method public void interrupt();
method public static boolean isAccessibilityButtonSupported();
method public boolean isEnabled();
+ method public boolean isObservedEventType(int);
method public boolean isTouchExplorationEnabled();
method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
diff --git a/api/test-current.txt b/api/test-current.txt
index 8647ed3..b181538 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -958,6 +958,15 @@
package android.view.accessibility {
+ public final class AccessibilityManager {
+ method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
+ method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
+ }
+
+ public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
+ method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
+ }
+
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 06a9b06..e0d60cd 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -47,8 +49,6 @@
import java.util.Collections;
import java.util.List;
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
/**
* This class describes an {@link AccessibilityService}. The system notifies an
* {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -554,7 +554,7 @@
}
/**
- * Updates the properties that an AccessibilitySerivice can change dynamically.
+ * Updates the properties that an AccessibilityService can change dynamically.
*
* @param other The info from which to update the properties.
*
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 6ea0825..8c47598 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -34,8 +34,6 @@
import libcore.io.IoUtils;
-import com.google.android.collect.Maps;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
@@ -139,7 +137,7 @@
Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
}
- Map map = null;
+ Map<String, Object> map = null;
StructStat stat = null;
try {
stat = Os.stat(mFile.getPath());
@@ -148,7 +146,7 @@
try {
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
- map = XmlUtils.readMapXml(str);
+ map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
@@ -214,12 +212,14 @@
}
}
+ @Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(mLock) {
mListeners.put(listener, CONTENT);
}
}
+ @Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(mLock) {
mListeners.remove(listener);
@@ -241,6 +241,7 @@
}
}
+ @Override
public Map<String, ?> getAll() {
synchronized (mLock) {
awaitLoadedLocked();
@@ -249,6 +250,7 @@
}
}
+ @Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
@@ -258,6 +260,7 @@
}
}
+ @Override
@Nullable
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
synchronized (mLock) {
@@ -267,6 +270,7 @@
}
}
+ @Override
public int getInt(String key, int defValue) {
synchronized (mLock) {
awaitLoadedLocked();
@@ -274,6 +278,7 @@
return v != null ? v : defValue;
}
}
+ @Override
public long getLong(String key, long defValue) {
synchronized (mLock) {
awaitLoadedLocked();
@@ -281,6 +286,7 @@
return v != null ? v : defValue;
}
}
+ @Override
public float getFloat(String key, float defValue) {
synchronized (mLock) {
awaitLoadedLocked();
@@ -288,6 +294,7 @@
return v != null ? v : defValue;
}
}
+ @Override
public boolean getBoolean(String key, boolean defValue) {
synchronized (mLock) {
awaitLoadedLocked();
@@ -296,6 +303,7 @@
}
}
+ @Override
public boolean contains(String key) {
synchronized (mLock) {
awaitLoadedLocked();
@@ -303,6 +311,7 @@
}
}
+ @Override
public Editor edit() {
// TODO: remove the need to call awaitLoadedLocked() when
// requesting an editor. will require some work on the
@@ -347,71 +356,81 @@
}
public final class EditorImpl implements Editor {
- private final Object mLock = new Object();
+ private final Object mEditorLock = new Object();
- @GuardedBy("mLock")
- private final Map<String, Object> mModified = Maps.newHashMap();
+ @GuardedBy("mEditorLock")
+ private final Map<String, Object> mModified = new HashMap<>();
- @GuardedBy("mLock")
+ @GuardedBy("mEditorLock")
private boolean mClear = false;
+ @Override
public Editor putString(String key, @Nullable String value) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
+ @Override
public Editor putStringSet(String key, @Nullable Set<String> values) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key,
(values == null) ? null : new HashSet<String>(values));
return this;
}
}
+ @Override
public Editor putInt(String key, int value) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
+ @Override
public Editor putLong(String key, long value) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
+ @Override
public Editor putFloat(String key, float value) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
+ @Override
public Editor putBoolean(String key, boolean value) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
+ @Override
public Editor remove(String key) {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mModified.put(key, this);
return this;
}
}
+ @Override
public Editor clear() {
- synchronized (mLock) {
+ synchronized (mEditorLock) {
mClear = true;
return this;
}
}
+ @Override
public void apply() {
final long startTime = System.currentTimeMillis();
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
+ @Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
@@ -429,6 +448,7 @@
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
+ @Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
@@ -471,13 +491,13 @@
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
- synchronized (mLock) {
+ synchronized (mEditorLock) {
boolean changesMade = false;
if (mClear) {
- if (!mMap.isEmpty()) {
+ if (!mapToWriteToDisk.isEmpty()) {
changesMade = true;
- mMap.clear();
+ mapToWriteToDisk.clear();
}
mClear = false;
}
@@ -489,18 +509,18 @@
// setting a value to "null" for a given key is specified to be
// equivalent to calling remove on that key.
if (v == this || v == null) {
- if (!mMap.containsKey(k)) {
+ if (!mapToWriteToDisk.containsKey(k)) {
continue;
}
- mMap.remove(k);
+ mapToWriteToDisk.remove(k);
} else {
- if (mMap.containsKey(k)) {
- Object existingValue = mMap.get(k);
+ if (mapToWriteToDisk.containsKey(k)) {
+ Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
- mMap.put(k, v);
+ mapToWriteToDisk.put(k, v);
}
changesMade = true;
@@ -522,6 +542,7 @@
mapToWriteToDisk);
}
+ @Override
public boolean commit() {
long startTime = 0;
@@ -564,11 +585,7 @@
}
} else {
// Run this function on the main thread.
- ActivityThread.sMainThreadHandler.post(new Runnable() {
- public void run() {
- notifyListeners(mcr);
- }
- });
+ ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr));
}
}
}
@@ -594,6 +611,7 @@
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
+ @Override
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr, isFromSyncCommit);
@@ -646,7 +664,7 @@
return str;
}
- // Note: must hold mWritingToDiskLock
+ @GuardedBy("mWritingToDiskLock")
private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {
long startTime = 0;
long existsTime = 0;
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index 8d81199..cdeee35 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -216,7 +216,7 @@
* @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
*/
public Slice getSlice() {
- if (getFormat() == FORMAT_ACTION) {
+ if (FORMAT_ACTION.equals(getFormat())) {
return ((Pair<PendingIntent, Slice>) mObj).second;
}
return (Slice) mObj;
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index b7e353a..52527ed 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -16,43 +16,48 @@
package android.hardware.location;
import android.annotation.RequiresPermission;
-import android.os.Handler;
+import android.os.RemoteException;
+
+import dalvik.system.CloseGuard;
import java.io.Closeable;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* A class describing a client of the Context Hub Service.
*
- * Clients can send messages to nanoapps at a Context Hub through this object.
+ * Clients can send messages to nanoapps at a Context Hub through this object. The APIs supported
+ * by this object are thread-safe and can be used without external synchronization.
*
* @hide
*/
public class ContextHubClient implements Closeable {
/*
- * The ContextHubClient interface associated with this client.
+ * The proxy to the client interface at the service.
*/
- // TODO: Implement this interface and associate with ContextHubClient object
- // private final IContextHubClient mClientInterface;
+ private final IContextHubClient mClientProxy;
/*
- * The listening callback associated with this client.
+ * The callback interface associated with this client.
*/
- private ContextHubClientCallback mCallback;
+ private final IContextHubClientCallback mCallbackInterface;
/*
* The Context Hub that this client is attached to.
*/
- private ContextHubInfo mAttachedHub;
+ private final ContextHubInfo mAttachedHub;
- /*
- * The handler to invoke mCallback.
- */
- private Handler mCallbackHandler;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
- ContextHubClient(ContextHubClientCallback callback, Handler handler, ContextHubInfo hubInfo) {
- mCallback = callback;
- mCallbackHandler = handler;
+ private final AtomicBoolean mIsClosed = new AtomicBoolean(false);
+
+ /* package */ ContextHubClient(
+ IContextHubClient clientProxy, IContextHubClientCallback callback,
+ ContextHubInfo hubInfo) {
+ mClientProxy = clientProxy;
+ mCallbackInterface = callback;
mAttachedHub = hubInfo;
+ mCloseGuard.open("close");
}
/**
@@ -71,7 +76,14 @@
* All futures messages targeted for this client are dropped at the service.
*/
public void close() {
- throw new UnsupportedOperationException("TODO: Implement this");
+ if (!mIsClosed.getAndSet(true)) {
+ mCloseGuard.close();
+ try {
+ mClientProxy.close();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -90,6 +102,22 @@
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
@ContextHubTransaction.Result
public int sendMessageToNanoApp(NanoAppMessage message) {
- throw new UnsupportedOperationException("TODO: Implement this");
+ try {
+ return mClientProxy.sendMessageToNanoApp(message);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ } finally {
+ super.finalize();
+ }
}
}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 2411727..b31c7bc 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -456,6 +456,54 @@
}
/**
+ * Creates an interface to the ContextHubClient to send down to the service.
+ *
+ * @param callback the callback to invoke at the client process
+ * @param handler the handler to post callbacks for this client
+ *
+ * @return the callback interface
+ */
+ private IContextHubClientCallback createClientCallback(
+ ContextHubClientCallback callback, Handler handler) {
+ return new IContextHubClientCallback.Stub() {
+ @Override
+ public void onMessageFromNanoApp(NanoAppMessage message) {
+ handler.post(() -> callback.onMessageFromNanoApp(message));
+ }
+
+ @Override
+ public void onHubReset() {
+ handler.post(() -> callback.onHubReset());
+ }
+
+ @Override
+ public void onNanoAppAborted(long nanoAppId, int abortCode) {
+ handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode));
+ }
+
+ @Override
+ public void onNanoAppLoaded(long nanoAppId) {
+ handler.post(() -> callback.onNanoAppLoaded(nanoAppId));
+ }
+
+ @Override
+ public void onNanoAppUnloaded(long nanoAppId) {
+ handler.post(() -> callback.onNanoAppUnloaded(nanoAppId));
+ }
+
+ @Override
+ public void onNanoAppEnabled(long nanoAppId) {
+ handler.post(() -> callback.onNanoAppEnabled(nanoAppId));
+ }
+
+ @Override
+ public void onNanoAppDisabled(long nanoAppId) {
+ handler.post(() -> callback.onNanoAppDisabled(nanoAppId));
+ }
+ };
+ }
+
+ /**
* Creates and registers a client and its callback with the Context Hub Service.
*
* A client is registered with the Context Hub Service for a specified Context Hub. When the
@@ -463,19 +511,37 @@
* {@link ContextHubClient} object, and receive notifications through the provided callback.
*
* @param callback the notification callback to register
- * @param hubInfo the hub to attach this client to
- * @param handler the handler to invoke the callback, if null uses the main thread's Looper
- *
+ * @param hubInfo the hub to attach this client to
+ * @param handler the handler to invoke the callback, if null uses the main thread's Looper
* @return the registered client object
*
- * @see ContextHubClientCallback
+ * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+ * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws NullPointerException if callback or hubInfo is null
*
* @hide
+ * @see ContextHubClientCallback
*/
public ContextHubClient createClient(
ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) {
- throw new UnsupportedOperationException(
- "TODO: Implement this, and throw an exception on error");
+ if (callback == null) {
+ throw new NullPointerException("Callback cannot be null");
+ }
+ if (hubInfo == null) {
+ throw new NullPointerException("Hub info cannot be null");
+ }
+
+ Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler;
+ IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler);
+
+ IContextHubClient client;
+ try {
+ client = mService.createClient(clientInterface, hubInfo.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return new ContextHubClient(client, clientInterface, hubInfo);
}
/**
diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl
new file mode 100644
index 0000000..d81126a
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClient.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * @hide
+ */
+interface IContextHubClient {
+
+ // Sends a message to a nanoapp
+ int sendMessageToNanoApp(in NanoAppMessage message);
+
+ // Closes the connection with the Context Hub
+ void close();
+}
diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl
new file mode 100644
index 0000000..1c76bcb
--- /dev/null
+++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * An interface used by the Context Hub Service to invoke callbacks for lifecycle notifications of a
+ * Context Hub and nanoapps, as well as for nanoapp messaging.
+ *
+ * @hide
+ */
+oneway interface IContextHubClientCallback {
+
+ // Callback invoked when receiving a message from a nanoapp.
+ void onMessageFromNanoApp(in NanoAppMessage message);
+
+ // Callback invoked when the attached Context Hub has reset.
+ void onHubReset();
+
+ // Callback invoked when a nanoapp aborts at the attached Context Hub.
+ void onNanoAppAborted(long nanoAppId, int abortCode);
+
+ // Callback invoked when a nanoapp is loaded at the attached Context Hub.
+ void onNanoAppLoaded(long nanoAppId);
+
+ // Callback invoked when a nanoapp is unloaded from the attached Context Hub.
+ void onNanoAppUnloaded(long nanoAppId);
+
+ // Callback invoked when a nanoapp is enabled at the attached Context Hub.
+ void onNanoAppEnabled(long nanoAppId);
+
+ // Callback invoked when a nanoapp is disabled at the attached Context Hub.
+ void onNanoAppDisabled(long nanoAppId);
+}
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ff8c1d0..1bb7c8f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,12 +17,14 @@
package android.hardware.location;
// Declare any non-default types here with import statements
-import android.hardware.location.ContextHubMessage;
import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubMessage;
import android.hardware.location.NanoApp;
-import android.hardware.location.NanoAppInstanceInfo;
import android.hardware.location.NanoAppFilter;
+import android.hardware.location.NanoAppInstanceInfo;
import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
/**
* @hide
@@ -52,4 +54,7 @@
// send a message to a nanoApp
int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg);
+
+ // Creates a client to send and receive messages
+ IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 13b9206..7836cd0 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,18 +21,16 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.Paint.Align;
import android.graphics.Region.Op;
+import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard.Key;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.GestureDetector;
@@ -986,6 +984,9 @@
private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) {
if (mAccessibilityManager.isEnabled()) {
+ if (!mAccessibilityManager.isObservedEventType(eventType)) {
+ return;
+ }
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
onInitializeAccessibilityEvent(event);
final String text;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 729c0ff..1e0948a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10456,6 +10456,15 @@
"storage_settings_clobber_threshold";
/**
+ * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF}
+ * temporarily for all users.
+ *
+ * @hide
+ */
+ public static final String LOCATION_GLOBAL_KILL_SWITCH =
+ "location_global_kill_switch";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 345e300..0525ab1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7208,7 +7208,8 @@
* @param text The announcement text.
*/
public void announceForAccessibility(CharSequence text) {
- if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
onInitializeAccessibilityEvent(event);
@@ -10967,7 +10968,8 @@
if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
invalidate();
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
event.setAction(action);
@@ -11792,7 +11794,8 @@
private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
int fromIndex, int toIndex) {
- if (mParent == null) {
+ if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) {
return;
}
AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -26182,7 +26185,8 @@
@Override
public void run() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_SCROLLED);
event.setScrollDeltaX(mDeltaX);
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 5adea66..1d19a9f 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.annotation.IntDef;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -23,6 +24,8 @@
import com.android.internal.util.BitUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -709,6 +712,38 @@
*/
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+ TYPE_VIEW_CLICKED,
+ TYPE_VIEW_LONG_CLICKED,
+ TYPE_VIEW_SELECTED,
+ TYPE_VIEW_FOCUSED,
+ TYPE_VIEW_TEXT_CHANGED,
+ TYPE_WINDOW_STATE_CHANGED,
+ TYPE_NOTIFICATION_STATE_CHANGED,
+ TYPE_VIEW_HOVER_ENTER,
+ TYPE_VIEW_HOVER_EXIT,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_WINDOW_CONTENT_CHANGED,
+ TYPE_VIEW_SCROLLED,
+ TYPE_VIEW_TEXT_SELECTION_CHANGED,
+ TYPE_ANNOUNCEMENT,
+ TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+ TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_INTERACTION_END,
+ TYPE_WINDOWS_CHANGED,
+ TYPE_VIEW_CONTEXT_CLICKED,
+ TYPE_ASSIST_READING_CONTEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
/**
* Mask for {@link AccessibilityEvent} all types.
*
@@ -741,7 +776,7 @@
private static final SynchronizedPool<AccessibilityEvent> sPool =
new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
- private int mEventType;
+ private @EventType int mEventType;
private CharSequence mPackageName;
private long mEventTime;
int mMovementGranularity;
@@ -833,7 +868,7 @@
*
* @return The event type.
*/
- public int getEventType() {
+ public @EventType int getEventType() {
return mEventType;
}
@@ -890,7 +925,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setEventType(int eventType) {
+ public void setEventType(@EventType int eventType) {
enforceNotSealed();
mEventType = eventType;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 35f6acb..0375635 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -187,6 +188,7 @@
*
* @hide
*/
+ @TestApi
public interface AccessibilityServicesStateChangeListener {
/**
@@ -452,6 +454,18 @@
}
/**
+ * Returns whether there are observers registered for this event type. If
+ * this method returns false you shuold not generate events of this type
+ * to conserve resources.
+ *
+ * @param type The event type.
+ * @return Whether the event is being observed.
+ */
+ public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+ return mIsEnabled && (mRelevantEventTypes & type) != 0;
+ }
+
+ /**
* Requests feedback interruption from all accessibility services.
*/
public void interrupt() {
@@ -683,6 +697,7 @@
* for a callback on the process's main handler.
* @hide
*/
+ @TestApi
public void addAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
synchronized (mLock) {
@@ -698,6 +713,7 @@
*
* @hide
*/
+ @TestApi
public void removeAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener) {
// Final CopyOnWriteArrayList - no lock needed.
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 4d3189e..b379280 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1952,7 +1952,8 @@
CharSequence beforeText = mInputText.getText();
if (!text.equals(beforeText.toString())) {
mInputText.setText(text);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
mInputText.onInitializeAccessibilityEvent(event);
@@ -2612,7 +2613,7 @@
}
private void sendAccessibilityEventForVirtualText(int eventType) {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
mInputText.onInitializeAccessibilityEvent(event);
mInputText.onPopulateAccessibilityEvent(event);
@@ -2623,7 +2624,7 @@
private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
String text) {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setClassName(Button.class.getName());
event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d9bc51f..71532a7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10836,6 +10836,10 @@
void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
int fromIndex, int removedCount, int addedCount) {
+ if (!AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
+ return;
+ }
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
event.setFromIndex(fromIndex);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index d807120..bfde6ac 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -504,7 +504,8 @@
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
- if (!accessibilityManager.isEnabled()) {
+ if (!accessibilityManager.isObservedEventType(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
return;
}
// treat toasts as notifications since they are used to
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index bab0306aa..5ec9094 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -113,6 +113,11 @@
void showGlobalActionsMenu();
/**
+ * Notifies the status bar that a new rotation suggestion is available.
+ */
+ void onProposedRotationChanged(int rotation);
+
+ /**
* Set whether the top app currently hides the statusbar.
*
* @param hidesStatusBar whether it is being hidden
diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java
index c22be2c..9a6e542 100644
--- a/core/java/com/android/internal/util/RingBuffer.java
+++ b/core/java/com/android/internal/util/RingBuffer.java
@@ -60,6 +60,25 @@
mBuffer[indexOf(mCursor++)] = t;
}
+ /**
+ * Returns object of type <T> at the next writable slot, creating one if it is not already
+ * available. In case of any errors while creating the object, <code>null</code> will
+ * be returned.
+ */
+ public T getNextSlot() {
+ final int nextSlotIdx = indexOf(mCursor++);
+ T item = mBuffer[nextSlotIdx];
+ if (item == null) {
+ try {
+ item = (T) mBuffer.getClass().getComponentType().newInstance();
+ } catch (IllegalAccessException | InstantiationException e) {
+ return null;
+ }
+ mBuffer[nextSlotIdx] = item;
+ }
+ return item;
+ }
+
public T[] toArray() {
// Only generic way to create a T[] from another T[]
T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass());
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 50ad547..759a41a 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -186,6 +186,9 @@
}
final AccessibilityEvent event = createEvent(virtualViewId, eventType);
+ if (event == null) {
+ return false;
+ }
return parent.requestSendAccessibilityEvent(mView, event);
}
@@ -240,6 +243,9 @@
if (parent != null) {
final AccessibilityEvent event = createEvent(virtualViewId,
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ if (event == null) {
+ return;
+ }
event.setContentChangeTypes(changeTypes);
parent.requestSendAccessibilityEvent(mView, event);
}
@@ -305,6 +311,9 @@
* the specified item.
*/
private AccessibilityEvent createEventForHost(int eventType) {
+ if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+ return null;
+ }
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
mView.onInitializeAccessibilityEvent(event);
@@ -325,6 +334,9 @@
* the specified item.
*/
private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
+ if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+ return null;
+ }
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
event.setEnabled(true);
event.setClassName(DEFAULT_CLASS_NAME);
diff --git a/core/res/res/drawable/btn_colored_material.xml b/core/res/res/drawable/btn_colored_material.xml
index c3c5760..7ba21e8 100644
--- a/core/res/res/drawable/btn_colored_material.xml
+++ b/core/res/res/drawable/btn_colored_material.xml
@@ -23,7 +23,7 @@
<item>
<shape android:shape="rectangle"
android:tint="@color/btn_colored_background_material">
- <corners android:radius="@dimen/control_corner_material" />
+ <corners android:radius="?attr/buttonCornerRadius" />
<solid android:color="@color/white" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
diff --git a/core/res/res/drawable/btn_default_mtrl_shape.xml b/core/res/res/drawable/btn_default_mtrl_shape.xml
index 8a31d5e..9d9cd06 100644
--- a/core/res/res/drawable/btn_default_mtrl_shape.xml
+++ b/core/res/res/drawable/btn_default_mtrl_shape.xml
@@ -23,7 +23,7 @@
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle"
android:tint="?attr/colorButtonNormal">
- <corners android:radius="@dimen/control_corner_material" />
+ <corners android:radius="?attr/buttonCornerRadius" />
<solid android:color="@color/white" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9a0eafd..da8a9cc 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1016,6 +1016,9 @@
<!-- Style for the "neutral" buttons within button bars. -->
<attr name="buttonBarNeutralButtonStyle" format="reference" />
+ <!-- Corner radius of buttons. -->
+ <attr name="buttonCornerRadius" format="dimension" />
+
<!-- Style for the search query widget. -->
<attr name="searchViewStyle" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 65a7ec6..171f74f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3174,6 +3174,8 @@
<!-- Corner radius of system dialogs -->
<dimen name="config_dialogCornerRadius">2dp</dimen>
+ <!-- Corner radius of system buttons -->
+ <dimen name="config_buttonCornerRadius">@dimen/control_corner_material</dimen>
<!-- Controls whether system buttons use all caps for text -->
<bool name="config_buttonTextAllCaps">true</bool>
<!-- Name of the font family used for system buttons -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e370786..3dbb89c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2853,6 +2853,7 @@
<!-- @hide For use by platform and tools only. Developers should not specify this value. -->
<public name="compileSdkVersionCodename" />
<public name="screenReaderFocusable" />
+ <public name="buttonCornerRadius" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 3fa0d3b..39310a8 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -69,6 +69,7 @@
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonStyle">@style/Widget.DeviceDefault.Button</item>
<item name="buttonStyleSmall">@style/Widget.DeviceDefault.Button.Small</item>
@@ -224,6 +225,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -243,6 +245,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -264,6 +267,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -284,6 +288,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -303,6 +308,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<!-- Color palette -->
@@ -331,6 +337,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -349,6 +356,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -368,6 +376,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -403,6 +412,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -423,6 +433,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -441,6 +452,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -461,6 +473,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -480,6 +493,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -499,6 +513,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -518,6 +533,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -537,6 +553,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -556,6 +573,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -573,6 +591,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -590,6 +609,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -614,6 +634,7 @@
<item name="textAppearanceSmallPopupMenu">@style/TextAppearance.DeviceDefault.Widget.PopupMenu.Small</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonStyle">@style/Widget.DeviceDefault.Light.Button</item>
<item name="buttonStyleSmall">@style/Widget.DeviceDefault.Light.Button.Small</item>
@@ -762,6 +783,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -780,6 +802,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -799,6 +822,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -820,6 +844,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -840,6 +865,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -855,6 +881,7 @@
<item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar.AlertDialog</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
<item name="borderlessButtonStyle">@style/Widget.DeviceDefault.Light.Button.Borderless.Small</item>
@@ -885,6 +912,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -903,6 +931,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -922,6 +951,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -967,6 +997,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -987,6 +1018,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1005,6 +1037,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1025,6 +1058,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1044,6 +1078,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1061,6 +1096,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1078,6 +1114,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1088,6 +1125,7 @@
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
+ <item name="colorBackground">@color/background_device_default_light</item>
<item name="colorPrimary">@color/primary_device_default_settings_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
@@ -1107,6 +1145,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1125,6 +1164,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1153,6 +1193,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1171,6 +1212,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1189,6 +1231,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
@@ -1207,6 +1250,7 @@
<item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
</style>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index c317121..9e6b1ab 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -103,6 +103,7 @@
<item name="buttonStyleSmall">@style/Widget.Material.Button.Small</item>
<item name="buttonStyleInset">@style/Widget.Material.Button.Inset</item>
<item name="buttonStyleToggle">@style/Widget.Material.Button.Toggle</item>
+ <item name="buttonCornerRadius">@dimen/control_corner_material</item>
<item name="switchStyle">@style/Widget.Material.CompoundButton.Switch</item>
<item name="mediaRouteButtonStyle">@style/Widget.Material.MediaRouteButton</item>
@@ -472,6 +473,7 @@
<item name="buttonStyleSmall">@style/Widget.Material.Light.Button.Small</item>
<item name="buttonStyleInset">@style/Widget.Material.Light.Button.Inset</item>
<item name="buttonStyleToggle">@style/Widget.Material.Light.Button.Toggle</item>
+ <item name="buttonCornerRadius">@dimen/control_corner_material</item>
<item name="switchStyle">@style/Widget.Material.Light.CompoundButton.Switch</item>
<item name="mediaRouteButtonStyle">@style/Widget.Material.Light.MediaRouteButton</item>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 454d209..4ce6029 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -231,6 +231,7 @@
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
Settings.Global.LOW_BATTERY_SOUND,
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 83c82af..94a05b2 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -427,7 +427,6 @@
ATRACE_CALL();
constexpr const int kMaxIterations = 20;
- *out_last_reference = 0u;
for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
in_out_value->data != 0u && iteration < kMaxIterations;
iteration++) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a77c4b9..b033137 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -210,7 +210,7 @@
// are OR'd together with `in_out_flags`.
// `in_out_config` is populated with the configuration for which the resolved value was defined.
// `out_last_reference` is populated with the last reference ID before resolving to an actual
- // value.
+ // value. This is only initialized if the passed in `in_out_value` is a reference.
// Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
// it was not found.
ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index ab1a22e..567adfe 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -319,7 +319,7 @@
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
EXPECT_EQ(basic::R::integer::ref2, value.data);
- uint32_t last_ref;
+ uint32_t last_ref = 0u;
cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
ASSERT_NE(kInvalidCookie, cookie);
EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
@@ -342,7 +342,7 @@
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
EXPECT_EQ(basic::R::array::integerArray1, value.data);
- uint32_t last_ref;
+ uint32_t last_ref = 0u;
cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
ASSERT_NE(kInvalidCookie, cookie);
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
@@ -350,6 +350,25 @@
EXPECT_EQ(basic::R::array::integerArray1, last_ref);
}
+TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ ResTable_config selected_config;
+ memset(&selected_config, 0, sizeof(selected_config));
+
+ uint32_t flags = 0u;
+
+ // Create some kind of Res_value that is NOT a reference.
+ Res_value value;
+ value.dataType = Res_value::TYPE_STRING;
+ value.data = 0;
+
+ uint32_t last_ref = basic::R::string::test1;
+ EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref));
+ EXPECT_EQ(basic::R::string::test1, last_ref);
+}
+
static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
const ResTable_config& configuration) {
return configurations.count(configuration) > 0;
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 46d4ae7..845acc0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -110,7 +110,8 @@
// We only respect the nothingToDraw check when we are composing a layer. This
// ensures that we paint the layer even if it is not currently visible in the
// event that the properties change and it becomes visible.
- if (!renderNode->isRenderable() || (renderNode->nothingToDraw() && mComposeLayer)) {
+ if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
+ (renderNode->nothingToDraw() && mComposeLayer)) {
return;
}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 56a675a..15ea367 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -195,7 +195,12 @@
if(!handleObexPacket(packet)) {
return;
}
- if (!mHasBody) {
+ /* Don't Pre-Send continue when Remote requested for SRM
+ * Let the Application confirm.
+ */
+ if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+ + " not hasBody case: " + mHasBody);
+ if (!mHasBody && !mSrmEnabled) {
while ((!mGetOperation) && (!finalBitSet)) {
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
if (mPrivateInput.available() > 0) {
@@ -204,8 +209,13 @@
}
}
}
-
- while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
+ /* Don't Pre-Send continue when Remote requested for SRM
+ * Let the Application confirm.
+ */
+ if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
+ + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
+ while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
+ && (mPrivateInput.available() == 0)) {
sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
if (mPrivateInput.available() > 0) {
break;
diff --git a/packages/SettingsLib/res/xml/timezones.xml b/packages/SettingsLib/res/xml/timezones.xml
index 12d31cf..6a8d780 100644
--- a/packages/SettingsLib/res/xml/timezones.xml
+++ b/packages/SettingsLib/res/xml/timezones.xml
@@ -27,6 +27,7 @@
<timezone id="Atlantic/South_Georgia"></timezone>
<timezone id="Atlantic/Azores"></timezone>
<timezone id="Atlantic/Cape_Verde"></timezone>
+ <timezone id="Etc/UTC"></timezone>
<timezone id="Africa/Casablanca"></timezone>
<timezone id="Europe/London"></timezone>
<timezone id="Europe/Amsterdam"></timezone>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index ee7885d..0703330 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -18,7 +18,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -27,7 +26,6 @@
import com.android.settingslib.applications.InterestingConfigChanges;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -104,10 +102,10 @@
}
for (int i = 0; i < mCategories.size(); i++) {
DashboardCategory category = mCategories.get(i);
- for (int j = 0; j < category.tiles.size(); j++) {
- Tile tile = category.tiles.get(j);
+ for (int j = 0; j < category.getTilesCount(); j++) {
+ Tile tile = category.getTile(j);
if (tileBlacklist.contains(tile.intent.getComponent())) {
- category.tiles.remove(j--);
+ category.removeTile(j--);
}
}
}
@@ -181,7 +179,7 @@
newCategory = new DashboardCategory();
categoryByKeyMap.put(newCategoryKey, newCategory);
}
- newCategory.tiles.add(tile);
+ newCategory.addTile(tile);
}
}
}
@@ -198,7 +196,7 @@
synchronized void sortCategories(Context context,
Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
- sortCategoriesForExternalTiles(context, categoryEntry.getValue());
+ categoryEntry.getValue().sortTiles(context.getPackageName());
}
}
@@ -210,16 +208,16 @@
synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
final DashboardCategory category = categoryEntry.getValue();
- final int count = category.tiles.size();
+ final int count = category.getTilesCount();
final Set<ComponentName> components = new ArraySet<>();
for (int i = count - 1; i >= 0; i--) {
- final Tile tile = category.tiles.get(i);
+ final Tile tile = category.getTile(i);
if (tile.intent == null) {
continue;
}
final ComponentName tileComponent = tile.intent.getComponent();
if (components.contains(tileComponent)) {
- category.tiles.remove(i);
+ category.removeTile(i);
} else {
components.add(tileComponent);
}
@@ -234,28 +232,7 @@
*/
private synchronized void sortCategoriesForExternalTiles(Context context,
DashboardCategory dashboardCategory) {
- final String skipPackageName = context.getPackageName();
+ dashboardCategory.sortTiles(context.getPackageName());
- // Sort tiles based on [priority, package within priority]
- Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
- final String package1 = tile1.intent.getComponent().getPackageName();
- final String package2 = tile2.intent.getComponent().getPackageName();
- final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
- // First sort by priority
- final int priorityCompare = tile2.priority - tile1.priority;
- if (priorityCompare != 0) {
- return priorityCompare;
- }
- // Then sort by package name, skip package take precedence
- if (packageCompare != 0) {
- if (TextUtils.equals(package1, skipPackageName)) {
- return -1;
- }
- if (TextUtils.equals(package2, skipPackageName)) {
- return 1;
- }
- }
- return packageCompare;
- });
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index f6f8168..a966e82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -16,6 +16,8 @@
package com.android.settingslib.drawer;
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,6 +25,8 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
public class DashboardCategory implements Parcelable {
@@ -48,39 +52,59 @@
/**
* List of the category's children
*/
- public List<Tile> tiles = new ArrayList<>();
+ private List<Tile> mTiles = new ArrayList<>();
+ DashboardCategory(DashboardCategory in) {
+ if (in != null) {
+ title = in.title;
+ key = in.key;
+ priority = in.priority;
+ for (Tile tile : in.mTiles) {
+ mTiles.add(tile);
+ }
+ }
+ }
public DashboardCategory() {
// Empty
}
+ /**
+ * Get a copy of the list of the category's children.
+ *
+ * Note: the returned list serves as a read-only list. If tiles needs to be added or removed
+ * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
+ */
+ public List<Tile> getTiles() {
+ return Collections.unmodifiableList(mTiles);
+ }
+
public void addTile(Tile tile) {
- tiles.add(tile);
+ mTiles.add(tile);
}
public void addTile(int n, Tile tile) {
- tiles.add(n, tile);
+ mTiles.add(n, tile);
}
public void removeTile(Tile tile) {
- tiles.remove(tile);
+ mTiles.remove(tile);
}
public void removeTile(int n) {
- tiles.remove(n);
+ mTiles.remove(n);
}
public int getTilesCount() {
- return tiles.size();
+ return mTiles.size();
}
public Tile getTile(int n) {
- return tiles.get(n);
+ return mTiles.get(n);
}
public boolean containsComponent(ComponentName component) {
- for (Tile tile : tiles) {
+ for (Tile tile : mTiles) {
if (TextUtils.equals(tile.intent.getComponent().getClassName(),
component.getClassName())) {
if (DEBUG) {
@@ -95,6 +119,40 @@
return false;
}
+ /**
+ * Sort priority value for tiles in this category.
+ */
+ public void sortTiles() {
+ Collections.sort(mTiles, TILE_COMPARATOR);
+ }
+
+ /**
+ * Sort priority value and package name for tiles in this category.
+ */
+ public void sortTiles(String skipPackageName) {
+ // Sort mTiles based on [priority, package within priority]
+ Collections.sort(mTiles, (tile1, tile2) -> {
+ final String package1 = tile1.intent.getComponent().getPackageName();
+ final String package2 = tile2.intent.getComponent().getPackageName();
+ final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
+ // First sort by priority
+ final int priorityCompare = tile2.priority - tile1.priority;
+ if (priorityCompare != 0) {
+ return priorityCompare;
+ }
+ // Then sort by package name, skip package take precedence
+ if (packageCompare != 0) {
+ if (TextUtils.equals(package1, skipPackageName)) {
+ return -1;
+ }
+ if (TextUtils.equals(package2, skipPackageName)) {
+ return 1;
+ }
+ }
+ return packageCompare;
+ });
+ }
+
@Override
public int describeContents() {
return 0;
@@ -106,11 +164,11 @@
dest.writeString(key);
dest.writeInt(priority);
- final int count = tiles.size();
+ final int count = mTiles.size();
dest.writeInt(count);
for (int n = 0; n < count; n++) {
- Tile tile = tiles.get(n);
+ Tile tile = mTiles.get(n);
tile.writeToParcel(dest, flags);
}
}
@@ -124,7 +182,7 @@
for (int n = 0; n < count; n++) {
Tile tile = Tile.CREATOR.createFromParcel(in);
- tiles.add(tile);
+ mTiles.add(tile);
}
}
@@ -141,4 +199,13 @@
return new DashboardCategory[size];
}
};
+
+ public static final Comparator<Tile> TILE_COMPARATOR =
+ new Comparator<Tile>() {
+ @Override
+ public int compare(Tile lhs, Tile rhs) {
+ return rhs.priority - lhs.priority;
+ }
+ };
+
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 038dcf8..e986e0f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -253,7 +253,7 @@
}
ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
for (DashboardCategory category : categories) {
- Collections.sort(category.tiles, TILE_COMPARATOR);
+ category.sortTiles();
}
Collections.sort(categories, CATEGORY_COMPARATOR);
if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
@@ -595,14 +595,6 @@
return pathSegments.get(0);
}
- public static final Comparator<Tile> TILE_COMPARATOR =
- new Comparator<Tile>() {
- @Override
- public int compare(Tile lhs, Tile rhs) {
- return rhs.priority - lhs.priority;
- }
- };
-
private static final Comparator<DashboardCategory> CATEGORY_COMPARATOR =
new Comparator<DashboardCategory>() {
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index d7eae5f..f099c90 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -95,8 +95,9 @@
mCategoryManager.backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
- assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).tiles.size()).isEqualTo(1);
- assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+ assertThat(
+ mCategoryByKeyMap.get(CategoryKey.CATEGORY_ACCOUNT).getTilesCount()).isEqualTo(1);
+ assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
}
@Test
@@ -114,9 +115,10 @@
// Added 1 more category to category map.
assertThat(mCategoryByKeyMap.size()).isEqualTo(2);
// The new category map has CATEGORY_NETWORK type now, which contains 1 tile.
- assertThat(mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).tiles.size()).isEqualTo(1);
+ assertThat(
+ mCategoryByKeyMap.get(CategoryKey.CATEGORY_NETWORK).getTilesCount()).isEqualTo(1);
// Old category still exists.
- assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
+ assertThat(mCategoryByKeyMap.get(oldCategory).getTilesCount()).isEqualTo(1);
}
@Test
@@ -136,9 +138,9 @@
tile3.intent =
new Intent().setComponent(new ComponentName(testPackage, "class3"));
tile3.priority = 200;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
// Sort their priorities
@@ -146,9 +148,9 @@
mCategoryByKeyMap);
// Verify they are now sorted.
- assertThat(category.tiles.get(0)).isSameAs(tile3);
- assertThat(category.tiles.get(1)).isSameAs(tile1);
- assertThat(category.tiles.get(2)).isSameAs(tile2);
+ assertThat(category.getTile(0)).isSameAs(tile3);
+ assertThat(category.getTile(1)).isSameAs(tile1);
+ assertThat(category.getTile(2)).isSameAs(tile2);
}
@Test
@@ -169,9 +171,9 @@
tile3.intent =
new Intent().setComponent(new ComponentName(testPackage1, "class3"));
tile3.priority = 50;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
// Sort their priorities
@@ -179,9 +181,9 @@
mCategoryByKeyMap);
// Verify they are now sorted.
- assertThat(category.tiles.get(0)).isSameAs(tile2);
- assertThat(category.tiles.get(1)).isSameAs(tile1);
- assertThat(category.tiles.get(2)).isSameAs(tile3);
+ assertThat(category.getTile(0)).isSameAs(tile2);
+ assertThat(category.getTile(1)).isSameAs(tile1);
+ assertThat(category.getTile(2)).isSameAs(tile3);
}
@Test
@@ -202,9 +204,9 @@
tile3.intent =
new Intent().setComponent(new ComponentName(testPackage, "class3"));
tile3.priority = 50;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
// Sort their priorities
@@ -212,9 +214,9 @@
mCategoryByKeyMap);
// Verify the sorting order is not changed
- assertThat(category.tiles.get(0)).isSameAs(tile1);
- assertThat(category.tiles.get(1)).isSameAs(tile2);
- assertThat(category.tiles.get(2)).isSameAs(tile3);
+ assertThat(category.getTile(0)).isSameAs(tile1);
+ assertThat(category.getTile(1)).isSameAs(tile2);
+ assertThat(category.getTile(2)).isSameAs(tile3);
}
@Test
@@ -236,10 +238,10 @@
final Tile tile4 = new Tile();
tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
tile4.priority = -1;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
- category.tiles.add(tile4);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
+ category.addTile(tile4);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
// Sort their priorities
@@ -247,10 +249,10 @@
mCategoryByKeyMap);
// Verify the sorting order is not changed
- assertThat(category.tiles.get(0)).isSameAs(tile1);
- assertThat(category.tiles.get(1)).isSameAs(tile2);
- assertThat(category.tiles.get(2)).isSameAs(tile3);
- assertThat(category.tiles.get(3)).isSameAs(tile4);
+ assertThat(category.getTile(0)).isSameAs(tile1);
+ assertThat(category.getTile(1)).isSameAs(tile2);
+ assertThat(category.getTile(2)).isSameAs(tile3);
+ assertThat(category.getTile(3)).isSameAs(tile4);
}
@Test
@@ -270,9 +272,9 @@
final Tile tile3 = new Tile();
tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
tile3.priority = 1;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
// Sort their priorities
@@ -280,9 +282,9 @@
mCategoryByKeyMap);
// Verify the sorting order is internal first, follow by package name ordering
- assertThat(category.tiles.get(0)).isSameAs(tile2);
- assertThat(category.tiles.get(1)).isSameAs(tile3);
- assertThat(category.tiles.get(2)).isSameAs(tile1);
+ assertThat(category.getTile(0)).isSameAs(tile2);
+ assertThat(category.getTile(1)).isSameAs(tile3);
+ assertThat(category.getTile(2)).isSameAs(tile1);
}
@Test
@@ -303,14 +305,14 @@
tile3.intent =
new Intent().setComponent(new ComponentName(testPackage, "class3"));
tile3.priority = 50;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
- assertThat(category.tiles.size()).isEqualTo(3);
+ assertThat(category.getTilesCount()).isEqualTo(3);
}
@Test
@@ -331,13 +333,13 @@
tile3.intent =
new Intent().setComponent(new ComponentName(testPackage, "class1"));
tile3.priority = 50;
- category.tiles.add(tile1);
- category.tiles.add(tile2);
- category.tiles.add(tile3);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
- assertThat(category.tiles.size()).isEqualTo(1);
+ assertThat(category.getTilesCount()).isEqualTo(1);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index dad3a28..a395a4a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -216,7 +216,7 @@
List<DashboardCategory> categoryList = TileUtils.getCategories(
mContext, cache, false /* categoryDefinedInManifest */, testAction,
TileUtils.SETTING_PKG);
- assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory);
+ assertThat(categoryList.get(0).getTile(0).category).isEqualTo(testCategory);
}
@Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 258c96c..7fb6ede 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -61,6 +61,7 @@
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -343,7 +344,8 @@
}
case Settings.CALL_METHOD_GET_SECURE: {
- Setting setting = getSecureSetting(name, requestingUserId);
+ Setting setting = getSecureSetting(name, requestingUserId,
+ /*enableOverride=*/ true);
return packageValueForCallResult(setting, isTrackingGeneration(args));
}
@@ -1073,6 +1075,10 @@
}
private Setting getSecureSetting(String name, int requestingUserId) {
+ return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false);
+ }
+
+ private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) {
if (DEBUG) {
Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
}
@@ -1102,6 +1108,14 @@
return getSsaidSettingLocked(callingPkg, owningUserId);
}
}
+ if (enableOverride) {
+ if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
+ final Setting overridden = getLocationProvidersAllowedSetting(owningUserId);
+ if (overridden != null) {
+ return overridden;
+ }
+ }
+ }
// Not the SSAID; do a straight lookup
synchronized (mLock) {
@@ -1190,6 +1204,35 @@
return null;
}
+ private Setting getLocationProvidersAllowedSetting(int owningUserId) {
+ synchronized (mLock) {
+ final Setting setting = getGlobalSetting(
+ Global.LOCATION_GLOBAL_KILL_SWITCH);
+ if (!"1".equals(setting.getValue())) {
+ return null;
+ }
+ // Global kill-switch is enabled. Return an empty value.
+ final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_SECURE, owningUserId);
+ return settingsState.new Setting(
+ Secure.LOCATION_PROVIDERS_ALLOWED,
+ "", // value
+ "", // tag
+ "", // default value
+ "", // package name
+ false, // from system
+ "0" // id
+ ) {
+ @Override
+ public boolean update(String value, boolean setDefault, String packageName,
+ String tag, boolean forceNonSystemPackage) {
+ Slog.wtf(LOG_TAG, "update shoudln't be called on this instance.");
+ return false;
+ }
+ };
+ }
+ }
+
private boolean insertSecureSetting(String name, String value, String tag,
boolean makeDefault, int requestingUserId, boolean forceNotify) {
if (DEBUG) {
@@ -2780,6 +2823,12 @@
}
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
+
+ // When the global kill switch is updated, send the change notification for
+ // the location setting.
+ if (isGlobalSettingsKey(key) && Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)) {
+ notifyLocationChangeForRunningUsers();
+ }
}
private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
@@ -2799,6 +2848,24 @@
}
}
+ private void notifyLocationChangeForRunningUsers() {
+ final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true);
+
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+
+ if (!mUserManager.isUserRunning(UserHandle.of(userId))) {
+ continue;
+ }
+
+ final int key = makeKey(SETTINGS_TYPE_GLOBAL, userId);
+ final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED);
+
+ mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
+ userId, 0, uri).sendToTarget();
+ }
+ }
+
private boolean isGlobalSettingsKey(int key) {
return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
}
@@ -2885,7 +2952,7 @@
} catch (SecurityException e) {
Slog.w(LOG_TAG, "Failed to notify for " + userId + ": " + uri, e);
}
- if (DEBUG) {
+ if (DEBUG || true) {
Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri);
}
} break;
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 12f75bb..0219db3 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -307,8 +307,9 @@
void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
int removedCount, int addedCount) {
- if (AccessibilityManager.getInstance(mContext).isEnabled() &&
- (isFocused() || isSelected() && isShown())) {
+ if (AccessibilityManager.getInstance(mContext).isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
+ && (isFocused() || isSelected() && isShown())) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
event.setFromIndex(fromIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..51175d1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -387,7 +387,9 @@
}
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: {
- if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
+ if (mAccessibilityManager.isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
+ && !mSendingHoverAccessibilityEvents) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
event.setImportantForAccessibility(true);
@@ -400,7 +402,9 @@
break;
}
case MotionEvent.ACTION_HOVER_EXIT: {
- if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
+ if (mAccessibilityManager.isObservedEventType(
+ AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
+ && mSendingHoverAccessibilityEvents) {
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
event.setImportantForAccessibility(true);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index eef43d2..a984680 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -625,9 +625,7 @@
@Override
public void onTaskStackChanged() {
if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
- if (!checkCurrentUserId(mContext, DEBUG)) {
- return;
- }
+
if (getState() != STATE_NO_PIP) {
boolean hasPip = false;
@@ -662,9 +660,7 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
- if (!checkCurrentUserId(mContext, DEBUG)) {
- return;
- }
+
StackInfo stackInfo = getPinnedStackInfo();
if (stackInfo == null) {
Log.w(TAG, "Cannot find pinned stack");
@@ -690,9 +686,7 @@
@Override
public void onPinnedActivityRestartAttempt(boolean clearedTask) {
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
- if (!checkCurrentUserId(mContext, DEBUG)) {
- return;
- }
+
// If PIPed activity is launched again by Launcher or intent, make it fullscreen.
movePipToFullscreen();
}
@@ -700,9 +694,7 @@
@Override
public void onPinnedStackAnimationEnded() {
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
- if (!checkCurrentUserId(mContext, DEBUG)) {
- return;
- }
+
switch (getState()) {
case STATE_PIP_MENU:
showPipMenu();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index cf3cae5..2d3080b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -68,7 +68,6 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.os.BackgroundThread;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsImpl;
@@ -372,7 +371,7 @@
if (mIam == null) return false;
try {
- return mIam.isInLockTaskMode();
+ return mIam.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6349275..8e1b104 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -82,6 +82,7 @@
private static final int MSG_TOGGLE_PANEL = 35 << MSG_SHIFT;
private static final int MSG_SHOW_SHUTDOWN_UI = 36 << MSG_SHIFT;
private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR = 37 << MSG_SHIFT;
+ private static final int MSG_ROTATION_PROPOSAL = 38 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -142,6 +143,8 @@
default void handleSystemKey(int arg1) { }
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
+
+ default void onRotationProposal(int rotation) { }
}
@VisibleForTesting
@@ -458,6 +461,15 @@
}
}
+ @Override
+ public void onProposedRotationChanged(int rotation) {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
+ mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0,
+ null).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -654,6 +666,11 @@
mCallbacks.get(i).setTopAppHidesStatusBar(msg.arg1 != 0);
}
break;
+ case MSG_ROTATION_PROPOSAL:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).onRotationProposal(msg.arg1);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d41e20..383d327 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -441,7 +441,8 @@
.withEndAction(() -> mDialog.dismiss())
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.start();
- if (mAccessibilityMgr.isEnabled()) {
+ if (mAccessibilityMgr.isObservedEventType(
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
event.setPackageName(mContext.getPackageName());
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 7e94d7b..22d922b 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;
@@ -65,6 +62,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -106,7 +104,7 @@
int mFeedbackType;
- Set<String> mPackageNames = new HashSet<>();
+ final Set<String> mPackageNames = new HashSet<>();
boolean mIsDefault;
@@ -284,40 +282,98 @@
return true;
}
- public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
- mEventTypes = info.eventTypes;
- mFeedbackType = info.feedbackType;
- String[] packageNames = info.packageNames;
- if (packageNames != null) {
- mPackageNames.addAll(Arrays.asList(packageNames));
+ boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
+ boolean somethingChanged = false;
+
+ if (mEventTypes != info.eventTypes) {
+ mEventTypes = info.eventTypes;
+ somethingChanged = true;
}
- mNotificationTimeout = info.notificationTimeout;
- mIsDefault = (info.flags & DEFAULT) != 0;
+
+ if (mFeedbackType != info.feedbackType) {
+ mFeedbackType = info.feedbackType;
+ somethingChanged = true;
+ }
+
+ final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]);
+ if (!Arrays.equals(oldPackageNames, info.packageNames)) {
+ mPackageNames.clear();
+ if (info.packageNames != null) {
+ Collections.addAll(mPackageNames, info.packageNames);
+ }
+ somethingChanged = true;
+ }
+
+ if (mNotificationTimeout != info.notificationTimeout) {
+ mNotificationTimeout = info.notificationTimeout;
+ somethingChanged = true;
+ }
+
+ final boolean newIsDefault = (info.flags & DEFAULT) != 0;
+ if (mIsDefault != newIsDefault) {
+ mIsDefault = newIsDefault;
+ somethingChanged = true;
+ }
if (supportsFlagForNotImportantViews(info)) {
- if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- } else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+ somethingChanged |= updateFetchFlag(info.flags,
+ AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
+ }
+
+ somethingChanged |= updateFetchFlag(info.flags,
+ AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS);
+
+ final boolean newRequestTouchExplorationMode = (info.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+ if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) {
+ mRequestTouchExplorationMode = newRequestTouchExplorationMode;
+ somethingChanged = true;
+ }
+
+ final boolean newRequestFilterKeyEvents = (info.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+ if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) {
+ mRequestFilterKeyEvents = newRequestFilterKeyEvents;
+ somethingChanged = true;
+ }
+
+ final boolean newRetrieveInteractiveWindows = (info.flags
+ & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+ if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) {
+ mRetrieveInteractiveWindows = newRetrieveInteractiveWindows;
+ somethingChanged = true;
+ }
+
+ final boolean newCaptureFingerprintGestures = (info.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
+ if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) {
+ mCaptureFingerprintGestures = newCaptureFingerprintGestures;
+ somethingChanged = true;
+ }
+
+ final boolean newRequestAccessibilityButton = (info.flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ if (mRequestAccessibilityButton != newRequestAccessibilityButton) {
+ mRequestAccessibilityButton = newRequestAccessibilityButton;
+ somethingChanged = true;
+ }
+
+ return somethingChanged;
+ }
+
+ private boolean updateFetchFlag(int allFlags, int flagToUpdate) {
+ if ((allFlags & flagToUpdate) != 0) {
+ if ((mFetchFlags & flagToUpdate) == 0) {
+ mFetchFlags |= flagToUpdate;
+ return true;
+ }
+ } else {
+ if ((mFetchFlags & flagToUpdate) != 0) {
+ mFetchFlags &= ~flagToUpdate;
+ return true;
}
}
-
- if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
- mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
- } else {
- mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
- }
-
- mRequestTouchExplorationMode = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
- mRequestFilterKeyEvents = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
- mRetrieveInteractiveWindows = (info.flags
- & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
- mCaptureFingerprintGestures = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
- mRequestAccessibilityButton = (info.flags
- & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+ return false;
}
protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -349,14 +405,15 @@
// If the XML manifest had data to configure the service its info
// should be already set. In such a case update only the dynamically
// configurable properties.
+ final boolean serviceInfoChanged;
AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
if (oldInfo != null) {
oldInfo.updateDynamicallyConfigurableProperties(info);
- setDynamicallyConfigurableProperties(oldInfo);
+ serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo);
} else {
- setDynamicallyConfigurableProperties(info);
+ serviceInfoChanged = setDynamicallyConfigurableProperties(info);
}
- mSystemSupport.onClientChange(true);
+ mSystemSupport.onClientChange(serviceInfoChanged);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3554448..8b5c85a7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2400,7 +2400,8 @@
private void announceNewUserIfNeeded() {
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
- if (userState.isHandlingAccessibilityEvents()) {
+ if (userState.isHandlingAccessibilityEvents()
+ && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) {
UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
String message = mContext.getString(R.string.user_switched,
@@ -3157,13 +3158,21 @@
if (mWindowsForAccessibilityCallback == null) {
return;
}
+ final int userId;
+ synchronized (mLock) {
+ userId = mCurrentUserId;
+ final UserState userState = getUserStateLocked(userId);
+ if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) {
+ return;
+ }
+ }
final long identity = Binder.clearCallingIdentity();
try {
// Let the client know the windows changed.
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_WINDOWS_CHANGED);
event.setEventTime(SystemClock.uptimeMillis());
- sendAccessibilityEvent(event, mCurrentUserId);
+ sendAccessibilityEvent(event, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -3368,6 +3377,10 @@
mUserId = userId;
}
+ public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
+ return (mLastSentRelevantEventTypes & type) != 0;
+ }
+
public int getClientState() {
int clientState = 0;
final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3419b80..62017e8 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -791,7 +791,7 @@
*/
private void sendAccessibilityEvent(int type) {
AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
- if (accessibilityManager.isEnabled()) {
+ if (accessibilityManager.isObservedEventType(type)) {
AccessibilityEvent event = AccessibilityEvent.obtain(type);
event.setWindowId(mAms.getActiveWindowId());
accessibilityManager.sendAccessibilityEvent(event);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index e87b4e6..d77e1a2 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -250,7 +250,24 @@
}
/**
- * @return whether the requested task is allowed to be launched.
+ * @return whether the requested task is allowed to be locked (either whitelisted, or declares
+ * lockTaskMode="always" in the manifest).
+ */
+ boolean isTaskWhitelisted(TaskRecord task) {
+ switch(task.mLockTaskAuth) {
+ case LOCK_TASK_AUTH_WHITELISTED:
+ case LOCK_TASK_AUTH_LAUNCHABLE:
+ case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
+ return true;
+ case LOCK_TASK_AUTH_PINNABLE:
+ case LOCK_TASK_AUTH_DONT_LOCK:
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * @return whether the requested task is disallowed to be launched.
*/
boolean isLockTaskModeViolation(TaskRecord task) {
return isLockTaskModeViolation(task, false);
@@ -258,7 +275,7 @@
/**
* @param isNewClearTask whether the task would be cleared as part of the operation.
- * @return whether the requested task is allowed to be launched.
+ * @return whether the requested task is disallowed to be launched.
*/
boolean isLockTaskModeViolation(TaskRecord task, boolean isNewClearTask) {
if (isLockTaskModeViolationInternal(task, isNewClearTask)) {
@@ -275,21 +292,18 @@
// If the task is already at the top and won't be cleared, then allow the operation
return false;
}
- final int lockTaskAuth = task.mLockTaskAuth;
- switch (lockTaskAuth) {
- case LOCK_TASK_AUTH_DONT_LOCK:
- return !mLockTaskModeTasks.isEmpty();
- case LOCK_TASK_AUTH_LAUNCHABLE_PRIV:
- case LOCK_TASK_AUTH_LAUNCHABLE:
- case LOCK_TASK_AUTH_WHITELISTED:
- return false;
- case LOCK_TASK_AUTH_PINNABLE:
- // Pinnable tasks can't be launched on top of locktask tasks.
- return !mLockTaskModeTasks.isEmpty();
- default:
- Slog.w(TAG, "isLockTaskModeViolation: invalid lockTaskAuth value=" + lockTaskAuth);
- return true;
+
+ // Allow recents activity if enabled by policy
+ if (task.isActivityTypeRecents() && isRecentsAllowed(task.userId)) {
+ return false;
}
+
+ return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
+ }
+
+ private boolean isRecentsAllowed(int userId) {
+ return (getLockTaskFeaturesForUser(userId)
+ & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
}
/**
@@ -491,6 +505,7 @@
}
if (mLockTaskModeTasks.isEmpty()) {
+ mSupervisor.mRecentTasks.onLockTaskModeStateChanged(lockTaskModeState, task.userId);
// Start lock task on the handler thread
mHandler.post(() -> performStartLockTask(
task.intent.getComponent().getPackageName(),
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1a19601..6fb3dbb 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -40,7 +40,7 @@
/**
* Activity manager code dealing with processes.
*/
-final class ProcessList {
+public final class ProcessList {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
// The minimum time we allow between crashes, for us to consider this
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index ebcf8c2..abb296e 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -502,6 +502,18 @@
}
}
+ void onLockTaskModeStateChanged(int lockTaskModeState, int userId) {
+ if (lockTaskModeState != ActivityManager.LOCK_TASK_MODE_LOCKED) {
+ return;
+ }
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskRecord tr = mTasks.get(i);
+ if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) {
+ remove(tr);
+ }
+ }
+ }
+
void removeTasksByPackageName(String packageName, int userId) {
for (int i = mTasks.size() - 1; i >= 0; --i) {
final TaskRecord tr = mTasks.get(i);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 90888f0..2c6fe94 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -34,6 +34,7 @@
import android.os.BatteryManager;
import android.os.Environment;
import android.os.Handler;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -162,7 +163,10 @@
UserHandle.USER_CURRENT);
mSensorListener = new SensorListener();
- mInjector.registerSensorListener(mContext, mSensorListener);
+
+ if (mInjector.isInteractive(mContext)) {
+ mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler);
+ }
mSettingsObserver = new SettingsObserver(mBgHandler);
mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
@@ -170,6 +174,8 @@
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SHUTDOWN);
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
mBroadcastReceiver = new Receiver();
mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
@@ -584,6 +590,11 @@
if (level != -1 && scale != 0) {
batteryLevelChanged(level, scale);
}
+ } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mInjector.unregisterSensorListener(mContext, mSensorListener);
+ } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mInjector.registerSensorListener(mContext, mSensorListener,
+ mInjector.getBackgroundHandler());
}
}
}
@@ -591,11 +602,11 @@
@VisibleForTesting
static class Injector {
public void registerSensorListener(Context context,
- SensorEventListener sensorListener) {
+ SensorEventListener sensorListener, Handler handler) {
SensorManager sensorManager = context.getSystemService(SensorManager.class);
Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
sensorManager.registerListener(sensorListener,
- lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
}
public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
@@ -675,5 +686,9 @@
public void cancelIdleJob(Context context) {
BrightnessIdleJob.cancelJob(context);
}
+
+ public boolean isInteractive(Context context) {
+ return context.getSystemService(PowerManager.class).isInteractive();
+ }
}
}
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
new file mode 100644
index 0000000..41d9feb
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.contexthub.V1_0.Result;
+import android.hardware.location.ContextHubTransaction;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle
+ * notification callbacks. This class implements the IContextHubClient object, and the implemented
+ * APIs must be thread-safe.
+ *
+ * @hide
+ */
+public class ContextHubClientBroker extends IContextHubClient.Stub
+ implements IBinder.DeathRecipient {
+ private static final String TAG = "ContextHubClientBroker";
+
+ /*
+ * The context of the service.
+ */
+ private final Context mContext;
+
+ /*
+ * The proxy to talk to the Context Hub HAL.
+ */
+ private final IContexthub mContextHubProxy;
+
+ /*
+ * The manager that registered this client.
+ */
+ private final ContextHubClientManager mClientManager;
+
+ /*
+ * The ID of the hub that this client is attached to.
+ */
+ private final int mAttachedContextHubId;
+
+ /*
+ * The host end point ID of this client.
+ */
+ private final short mHostEndPointId;
+
+ /*
+ * The remote callback interface for this client.
+ */
+ private final IContextHubClientCallback mCallbackInterface;
+
+ /*
+ * false if the connection has been closed by the client, true otherwise.
+ */
+ private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true);
+
+ /* package */ ContextHubClientBroker(
+ Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
+ int contextHubId, short hostEndPointId, IContextHubClientCallback callback) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ mClientManager = clientManager;
+ mAttachedContextHubId = contextHubId;
+ mHostEndPointId = hostEndPointId;
+ mCallbackInterface = callback;
+ }
+
+ /**
+ * Attaches a death recipient for this client
+ *
+ * @throws RemoteException if the client has already died
+ */
+ /* package */ void attachDeathRecipient() throws RemoteException {
+ mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+ }
+
+ /**
+ * Sends from this client to a nanoapp.
+ *
+ * @param message the message to send
+ * @return the error code of sending the message
+ */
+ @ContextHubTransaction.Result
+ @Override
+ public int sendMessageToNanoApp(NanoAppMessage message) {
+ ContextHubServiceUtil.checkPermissions(mContext);
+
+ int result;
+ if (mConnectionOpen.get()) {
+ ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(
+ mHostEndPointId, message);
+
+ try {
+ result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
+ + mAttachedContextHubId + ")", e);
+ result = Result.UNKNOWN_FAILURE;
+ }
+ } else {
+ Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
+ result = Result.UNKNOWN_FAILURE;
+ }
+
+ return ContextHubServiceUtil.toTransactionResult(result);
+ }
+
+ /**
+ * Closes the connection for this client with the service.
+ */
+ @Override
+ public void close() {
+ if (mConnectionOpen.getAndSet(false)) {
+ mClientManager.unregisterClient(mHostEndPointId);
+ }
+ }
+
+ /**
+ * Invoked when the underlying binder of this broker has died at the client process.
+ */
+ public void binderDied() {
+ try {
+ IContextHubClient.Stub.asInterface(this).close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while closing client on death", e);
+ }
+ }
+
+ /**
+ * @return the ID of the context hub this client is attached to
+ */
+ /* package */ int getAttachedContextHubId() {
+ return mAttachedContextHubId;
+ }
+
+ /**
+ * @return the host endpoint ID of this client
+ */
+ /* package */ short getHostEndPointId() {
+ return mHostEndPointId;
+ }
+
+ /**
+ * Sends a message to the client associated with this object.
+ *
+ * @param message the message that came from a nanoapp
+ */
+ /* package */ void sendMessageToClient(NanoAppMessage message) {
+ if (mConnectionOpen.get()) {
+ try {
+ mCallbackInterface.onMessageFromNanoApp(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = "
+ + mHostEndPointId + ")", e);
+ }
+ }
+ }
+
+ /**
+ * Handles a nanoapp load event.
+ *
+ * @param nanoAppId the ID of the nanoapp that was loaded.
+ */
+ /* package */ void onNanoAppLoaded(long nanoAppId) {
+ if (mConnectionOpen.get()) {
+ try {
+ mCallbackInterface.onNanoAppLoaded(nanoAppId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onNanoAppLoaded on client"
+ + " (host endpoint ID = " + mHostEndPointId + ")", e);
+ }
+ }
+ }
+
+ /**
+ * Handles a nanoapp unload event.
+ *
+ * @param nanoAppId the ID of the nanoapp that was unloaded.
+ */
+ /* package */ void onNanoAppUnloaded(long nanoAppId) {
+ if (mConnectionOpen.get()) {
+ try {
+ mCallbackInterface.onNanoAppUnloaded(nanoAppId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onNanoAppUnloaded on client"
+ + " (host endpoint ID = " + mHostEndPointId + ")", e);
+ }
+ }
+ }
+
+ /**
+ * Handles a hub reset for this client.
+ */
+ /* package */ void onHubReset() {
+ if (mConnectionOpen.get()) {
+ try {
+ mCallbackInterface.onHubReset();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling onHubReset on client" +
+ " (host endpoint ID = " + mHostEndPointId + ")", e);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
new file mode 100644
index 0000000..d58a746
--- /dev/null
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.contexthub.V1_0.ContextHubMsg;
+import android.hardware.contexthub.V1_0.IContexthub;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
+import android.hardware.location.NanoAppMessage;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+
+/**
+ * A class that manages registration/unregistration of clients and manages messages to/from clients.
+ *
+ * @hide
+ */
+/* package */ class ContextHubClientManager {
+ private static final String TAG = "ContextHubClientManager";
+
+ /*
+ * The maximum host endpoint ID value that a client can be assigned.
+ */
+ private static final int MAX_CLIENT_ID = 0x7fff;
+
+ /*
+ * Local flag to enable debug logging.
+ */
+ private static final boolean DEBUG_LOG_ENABLED = true;
+
+ /*
+ * The context of the service.
+ */
+ private final Context mContext;
+
+ /*
+ * The proxy to talk to the Context Hub.
+ */
+ private final IContexthub mContextHubProxy;
+
+ /*
+ * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients.
+ * A concurrent data structure is used since the registration/unregistration can occur in
+ * multiple threads.
+ */
+ private final ConcurrentHashMap<Short, ContextHubClientBroker> mHostEndPointIdToClientMap =
+ new ConcurrentHashMap<>();
+
+ /*
+ * The next host endpoint ID to start iterating for the next available host endpoint ID.
+ */
+ private int mNextHostEndpointId = 0;
+
+ /* package */ ContextHubClientManager(
+ Context context, IContexthub contextHubProxy) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ }
+
+ /**
+ * Registers a new client with the service.
+ *
+ * @param clientCallback the callback interface of the client to register
+ * @param contextHubId the ID of the hub this client is attached to
+ *
+ * @return the client interface
+ *
+ * @throws IllegalStateException if max number of clients have already registered
+ */
+ /* package */ IContextHubClient registerClient(
+ IContextHubClientCallback clientCallback, int contextHubId) {
+ ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId);
+
+ try {
+ broker.attachDeathRecipient();
+ } catch (RemoteException e) {
+ // The client process has died, so we close the connection and return null.
+ Log.e(TAG, "Failed to attach death recipient to client");
+ broker.close();
+ return null;
+ }
+
+ Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
+ return IContextHubClient.Stub.asInterface(broker);
+ }
+
+ /**
+ * Handles a message sent from a nanoapp.
+ *
+ * @param contextHubId the ID of the hub where the nanoapp sent the message from
+ * @param message the message send by a nanoapp
+ */
+ /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) {
+ NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
+
+ if (DEBUG_LOG_ENABLED) {
+ String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast";
+ Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x"
+ + Long.toHexString(clientMessage.getNanoAppId()));
+ }
+
+ if (clientMessage.isBroadcastMessage()) {
+ broadcastMessage(contextHubId, clientMessage);
+ } else {
+ ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
+ if (proxy != null) {
+ proxy.sendMessageToClient(clientMessage);
+ } else {
+ Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
+ + message.hostEndPoint + ")");
+ }
+ }
+ }
+
+ /**
+ * Unregisters a client from the service.
+ *
+ * This method should be invoked as a result of a client calling the ContextHubClient.close(),
+ * or if the client process has died.
+ *
+ * @param hostEndPointId the host endpoint ID of the client that has died
+ */
+ /* package */ void unregisterClient(short hostEndPointId) {
+ if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
+ Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
+ } else {
+ Log.e(TAG, "Cannot unregister non-existing client with host endpoint ID "
+ + hostEndPointId);
+ }
+ }
+
+ /**
+ * Handles a nanoapp load event.
+ *
+ * @param contextHubId the ID of the hub where the nanoapp was loaded.
+ * @param nanoAppId the ID of the nanoapp that was loaded.
+ */
+ /* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) {
+ forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId));
+ }
+
+ /**
+ * Handles a nanoapp unload event.
+ *
+ * @param contextHubId the ID of the hub where the nanoapp was unloaded.
+ * @param nanoAppId the ID of the nanoapp that was unloaded.
+ */
+ /* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) {
+ forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId));
+ }
+
+ /**
+ * Handles a hub reset.
+ *
+ * @param contextHubId the ID of the hub that has reset.
+ */
+ /* package */ void onHubReset(int contextHubId) {
+ forEachClientOfHub(contextHubId, client -> client.onHubReset());
+ }
+
+ /**
+ * Creates a new ContextHubClientBroker object for a client and registers it with the client
+ * manager.
+ *
+ * @param clientCallback the callback interface of the client to register
+ * @param contextHubId the ID of the hub this client is attached to
+ *
+ * @return the ContextHubClientBroker object
+ *
+ * @throws IllegalStateException if max number of clients have already registered
+ */
+ private synchronized ContextHubClientBroker createNewClientBroker(
+ IContextHubClientCallback clientCallback, int contextHubId) {
+ if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
+ throw new IllegalStateException("Could not register client - max limit exceeded");
+ }
+
+ ContextHubClientBroker broker = null;
+ int id = mNextHostEndpointId;
+ for (int i = 0; i <= MAX_CLIENT_ID; i++) {
+ if (!mHostEndPointIdToClientMap.containsKey(id)) {
+ broker = new ContextHubClientBroker(
+ mContext, mContextHubProxy, this, contextHubId, (short)id, clientCallback);
+ mHostEndPointIdToClientMap.put((short)id, broker);
+ mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+ break;
+ }
+
+ id = (id == MAX_CLIENT_ID) ? 0 : id + 1;
+ }
+
+ return broker;
+ }
+
+ /**
+ * Broadcasts a message from a nanoapp to all clients attached to the associated hub.
+ *
+ * @param contextHubId the ID of the hub where the nanoapp sent the message from
+ * @param message the message send by a nanoapp
+ */
+ private void broadcastMessage(int contextHubId, NanoAppMessage message) {
+ forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message));
+ }
+
+ /**
+ * Runs a command for each client that is attached to a hub with the given ID.
+ *
+ * @param contextHubId the ID of the hub
+ * @param callback the command to invoke for the client
+ */
+ private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) {
+ for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+ if (broker.getAttachedContextHubId() == contextHubId) {
+ callback.accept(broker);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index da481a8..e08c659 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,12 +16,10 @@
package com.android.server.location;
-import android.Manifest;
import android.content.Context;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
-import android.hardware.contexthub.V1_0.HostEndPoint;
import android.hardware.contexthub.V1_0.HubAppInfo;
import android.hardware.contexthub.V1_0.IContexthub;
import android.hardware.contexthub.V1_0.IContexthubCallback;
@@ -29,13 +27,17 @@
import android.hardware.contexthub.V1_0.TransactionResult;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
+import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubCallback;
+import android.hardware.location.IContextHubClient;
+import android.hardware.location.IContextHubClientCallback;
import android.hardware.location.IContextHubService;
import android.hardware.location.IContextHubTransactionCallback;
import android.hardware.location.NanoApp;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppFilter;
import android.hardware.location.NanoAppInstanceInfo;
+import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -49,7 +51,9 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
@@ -58,9 +62,6 @@
*/
public class ContextHubService extends IContextHubService.Stub {
private static final String TAG = "ContextHubService";
- private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
- private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
- + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
/*
* Constants for the type of transaction that is defined by ContextHubService.
@@ -104,6 +105,12 @@
// The manager for transaction queue
private final ContextHubTransactionManager mTransactionManager;
+ // The manager for sending messages to/from clients
+ private final ContextHubClientManager mClientManager;
+
+ // The default client for old API clients
+ private final Map<Integer, IContextHubClient> mDefaultClientMap;
+
/**
* Class extending the callback to register with a Context Hub.
*/
@@ -146,21 +153,34 @@
mContextHubProxy = getContextHubProxy();
if (mContextHubProxy == null) {
mTransactionManager = null;
+ mClientManager = null;
+ mDefaultClientMap = Collections.EMPTY_MAP;
mContextHubInfo = new ContextHubInfo[0];
return;
}
- mTransactionManager = new ContextHubTransactionManager(mContextHubProxy);
+ mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
+ mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager);
List<ContextHub> hubList;
try {
hubList = mContextHubProxy.getHubs();
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException while getting Context Hub info");
+ Log.e(TAG, "RemoteException while getting Context Hub info", e);
hubList = Collections.emptyList();
}
mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList);
+ HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
+ for (ContextHubInfo contextHubInfo : mContextHubInfo) {
+ int contextHubId = contextHubInfo.getId();
+
+ IContextHubClient client = mClientManager.registerClient(
+ createDefaultClientCallback(contextHubId), contextHubId);
+ defaultClientMap.put(contextHubId, client);
+ }
+ mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
+
for (ContextHubInfo contextHubInfo : mContextHubInfo) {
int contextHubId = contextHubInfo.getId();
try {
@@ -168,7 +188,7 @@
contextHubId, new ContextHubServiceCallback(contextHubId));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
- + contextHubId + ")");
+ + contextHubId + ")", e);
}
}
@@ -185,6 +205,53 @@
}
/**
+ * Creates a default client callback for old API clients.
+ *
+ * @param contextHubId the ID of the hub to attach this client to
+ * @return the internal callback interface
+ */
+ private IContextHubClientCallback createDefaultClientCallback(int contextHubId) {
+ return new IContextHubClientCallback.Stub() {
+ @Override
+ public void onMessageFromNanoApp(NanoAppMessage message) {
+ int nanoAppInstanceId =
+ mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ?
+ mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1;
+
+ onMessageReceiptOldApi(
+ message.getMessageType(), contextHubId, nanoAppInstanceId,
+ message.getMessageBody());
+ }
+
+ @Override
+ public void onHubReset() {
+ byte[] data = {TransactionResult.SUCCESS};
+ onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+ }
+
+ @Override
+ public void onNanoAppAborted(long nanoAppId, int abortCode) {
+ }
+
+ @Override
+ public void onNanoAppLoaded(long nanoAppId) {
+ }
+
+ @Override
+ public void onNanoAppUnloaded(long nanoAppId) {
+ }
+
+ @Override
+ public void onNanoAppEnabled(long nanoAppId) {
+ }
+
+ @Override
+ public void onNanoAppDisabled(long nanoAppId) {
+ }
+ };
+ }
+
+ /**
* @return the IContexthub proxy interface
*/
private IContexthub getContextHubProxy() {
@@ -192,7 +259,7 @@
try {
proxy = IContexthub.getService(true /* retry */);
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy");
+ Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
} catch (NoSuchElementException e) {
Log.i(TAG, "Context Hub HAL service not found");
}
@@ -204,6 +271,7 @@
public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
mCallbacksList.register(callback);
+
Log.d(TAG, "Added callback, total callbacks " +
mCallbacksList.getRegisteredCallbackCount());
return 0;
@@ -292,7 +360,7 @@
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
byte[] data = {(byte) result};
- onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
+ onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
}
};
}
@@ -449,41 +517,37 @@
return -1;
}
if (msg.getData() == null) {
- Log.w(TAG, "ContextHubMessage message body cannot be null");
+ Log.e(TAG, "ContextHubMessage message body cannot be null");
+ return -1;
+ }
+ if (!mDefaultClientMap.containsKey(hubHandle)) {
+ Log.e(TAG, "Hub with ID " + hubHandle + " does not exist");
return -1;
}
- int result;
+ boolean success = false;
if (nanoAppHandle == OS_APP_INSTANCE) {
if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
- result = queryNanoAppsInternal(hubHandle);
+ success = (queryNanoAppsInternal(hubHandle) == Result.OK);
} else {
Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
- result = Result.BAD_PARAMS;
}
} else {
NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
if (info != null) {
- ContextHubMsg hubMessage = new ContextHubMsg();
- hubMessage.appName = info.getAppId();
- hubMessage.msgType = msg.getMsgType();
- hubMessage.hostEndPoint = HostEndPoint.UNSPECIFIED;
- ContextHubServiceUtil.copyToByteArrayList(msg.getData(), hubMessage.msg);
+ NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
+ info.getAppId(), msg.getMsgType(), msg.getData());
- try {
- result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to send nanoapp message - RemoteException");
- result = Result.UNKNOWN_FAILURE;
- }
+ IContextHubClient client = mDefaultClientMap.get(hubHandle);
+ success = (client.sendMessageToNanoApp(message) ==
+ ContextHubTransaction.TRANSACTION_SUCCESS);
} else {
Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID "
+ nanoAppHandle + " does not exist.");
- result = Result.BAD_PARAMS;
}
}
- return (result == Result.OK ? 0 : -1);
+ return success ? 0 : -1;
}
/**
@@ -493,12 +557,7 @@
* @param message the message contents
*/
private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
- // TODO(b/67734082): Send to new API clients
- byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg);
-
- int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ?
- mNanoAppIdToInstanceMap.get(message.appName) : -1;
- onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data);
+ mClientManager.onMessageFromNanoApp(contextHubId, message);
}
/**
@@ -534,7 +593,7 @@
data[0] = (byte) result;
ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);
- onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
}
/**
@@ -552,7 +611,7 @@
byte[] data = new byte[1];
data[0] = (byte) result;
- onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
+ onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
}
/**
@@ -577,8 +636,7 @@
mTransactionManager.onHubReset();
queryNanoAppsInternal(contextHubId);
- byte[] data = {TransactionResult.SUCCESS};
- onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
+ mClientManager.onHubReset(contextHubId);
} else {
Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
+ eventType + ")");
@@ -641,6 +699,45 @@
}
}
+ /**
+ * @param contextHubId the hub ID to validate
+ * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
+ */
+ private boolean isValidContextHubId(int contextHubId) {
+ for (ContextHubInfo hubInfo : mContextHubInfo) {
+ if (hubInfo.getId() == contextHubId) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates and registers a client at the service for the specified Context Hub.
+ *
+ * @param clientCallback the client interface to register with the service
+ * @param contextHubId the ID of the hub this client is attached to
+ * @return the generated client interface, null if registration was unsuccessful
+ *
+ * @throws IllegalArgumentException if contextHubId is not a valid ID
+ * @throws IllegalStateException if max number of clients have already registered
+ * @throws NullPointerException if clientCallback is null
+ */
+ @Override
+ public IContextHubClient createClient(
+ IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException {
+ checkPermissions();
+ if (!isValidContextHubId(contextHubId)) {
+ throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+ }
+ if (clientCallback == null) {
+ throw new NullPointerException("Cannot register client with null callback");
+ }
+
+ return mClientManager.registerClient(clientCallback, contextHubId);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -664,10 +761,10 @@
}
private void checkPermissions() {
- mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+ ContextHubServiceUtil.checkPermissions(mContext);
}
- private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) {
+ private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) {
if (data == null) {
return -1;
}
diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
index ddbaf86..6faeb72 100644
--- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java
@@ -16,11 +16,15 @@
package com.android.server.location;
+import android.Manifest;
+import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHub;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
import android.hardware.contexthub.V1_0.HubAppInfo;
+import android.hardware.contexthub.V1_0.Result;
import android.hardware.location.ContextHubInfo;
+import android.hardware.location.ContextHubTransaction;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
@@ -34,6 +38,9 @@
*/
/* package */ class ContextHubServiceUtil {
private static final String TAG = "ContextHubServiceUtil";
+ private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+ private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+ + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
/**
* Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects.
@@ -165,4 +172,40 @@
message.appName, message.msgType, messageArray,
message.hostEndPoint == HostEndPoint.BROADCAST);
}
+
+ /**
+ * Checks for location hardware permissions.
+ *
+ * @param context the context of the service
+ */
+ /* package */
+ static void checkPermissions(Context context) {
+ context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+ }
+
+ /**
+ * Helper function to convert from the HAL Result enum error code to the
+ * ContextHubTransaction.Result type.
+ *
+ * @param halResult the Result enum error code
+ * @return the ContextHubTransaction.Result equivalent
+ */
+ @ContextHubTransaction.Result
+ /* package */
+ static int toTransactionResult(int halResult) {
+ switch (halResult) {
+ case Result.OK:
+ return ContextHubTransaction.TRANSACTION_SUCCESS;
+ case Result.BAD_PARAMS:
+ return ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS;
+ case Result.NOT_INIT:
+ return ContextHubTransaction.TRANSACTION_FAILED_UNINITIALIZED;
+ case Result.TRANSACTION_PENDING:
+ return ContextHubTransaction.TRANSACTION_FAILED_PENDING;
+ case Result.TRANSACTION_FAILED:
+ case Result.UNKNOWN_FAILURE:
+ default: /* fall through */
+ return ContextHubTransaction.TRANSACTION_FAILED_UNKNOWN;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 898b76c..47d9d56 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -58,6 +58,11 @@
private final IContexthub mContextHubProxy;
/*
+ * The manager for all clients for the service.
+ */
+ private final ContextHubClientManager mClientManager;
+
+ /*
* A queue containing the current transactions
*/
private final ArrayDeque<ContextHubServiceTransaction> mTransactionQueue = new ArrayDeque<>();
@@ -73,8 +78,10 @@
private final ScheduledThreadPoolExecutor mTimeoutExecutor = new ScheduledThreadPoolExecutor(1);
private ScheduledFuture<?> mTimeoutFuture = null;
- /* package */ ContextHubTransactionManager(IContexthub contextHubProxy) {
+ /* package */ ContextHubTransactionManager(
+ IContexthub contextHubProxy, ContextHubClientManager clientManager) {
mContextHubProxy = contextHubProxy;
+ mClientManager = clientManager;
}
/**
@@ -113,6 +120,9 @@
/* package */ void onTransactionComplete(int result) {
try {
onCompleteCallback.onTransactionComplete(result);
+ if (result == Result.OK) {
+ mClientManager.onNanoAppLoaded(contextHubId, nanoAppBinary.getNanoAppId());
+ }
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while calling client onTransactionComplete");
}
@@ -153,6 +163,9 @@
/* package */ void onTransactionComplete(int result) {
try {
onCompleteCallback.onTransactionComplete(result);
+ if (result == Result.OK) {
+ mClientManager.onNanoAppUnloaded(contextHubId, nanoAppId);
+ }
} catch (RemoteException e) {
Log.e(TAG, "RemoteException while calling client onTransactionComplete");
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
new file mode 100644
index 0000000..2bd9cab
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -0,0 +1,514 @@
+/*
+ * 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.net;
+
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
+
+import android.app.ActivityManager;
+import android.net.NetworkPolicyManager;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
+import com.android.server.am.ProcessList;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+public class NetworkPolicyLogger {
+ static final String TAG = "NetworkPolicy";
+
+ static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
+ static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private static final int MAX_LOG_SIZE =
+ ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
+ private static final int MAX_NETWORK_BLOCKED_LOG_SIZE =
+ ActivityManager.isLowRamDeviceStatic() ? 50 : 100;
+
+ private static final int EVENT_TYPE_GENERIC = 0;
+ private static final int EVENT_NETWORK_BLOCKED = 1;
+ private static final int EVENT_UID_STATE_CHANGED = 2;
+ private static final int EVENT_POLICIES_CHANGED = 3;
+ private static final int EVENT_METEREDNESS_CHANGED = 4;
+ private static final int EVENT_USER_STATE_REMOVED = 5;
+ private static final int EVENT_RESTRICT_BG_CHANGED = 6;
+ private static final int EVENT_DEVICE_IDLE_MODE_ENABLED = 7;
+ private static final int EVENT_APP_IDLE_STATE_CHANGED = 8;
+ private static final int EVENT_PAROLE_STATE_CHANGED = 9;
+ private static final int EVENT_TEMP_POWER_SAVE_WL_CHANGED = 10;
+ private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
+ private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
+
+ static final int NTWK_BLOCKED_POWER = 0;
+ static final int NTWK_ALLOWED_NON_METERED = 1;
+ static final int NTWK_BLOCKED_BLACKLIST = 2;
+ static final int NTWK_ALLOWED_WHITELIST = 3;
+ static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
+ static final int NTWK_BLOCKED_BG_RESTRICT = 5;
+ static final int NTWK_ALLOWED_DEFAULT = 6;
+
+ private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
+ private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
+ private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE);
+
+ private final Object mLock = new Object();
+
+ void networkBlocked(int uid, int reason) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, uid + " is " + getBlockedReason(reason));
+ mNetworkBlockedBuffer.networkBlocked(uid, reason);
+ }
+ }
+
+ void uidStateChanged(int uid, int procState, long procStateSeq) {
+ synchronized (mLock) {
+ if (LOGV) Slog.v(TAG,
+ uid + " state changed to " + procState + " with seq=" + procStateSeq);
+ mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq);
+ }
+ }
+
+ void event(String msg) {
+ synchronized (mLock) {
+ if (LOGV) Slog.v(TAG, msg);
+ mEventsBuffer.event(msg);
+ }
+ }
+
+ void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) {
+ synchronized (mLock) {
+ if (LOGV) Slog.v(TAG, getPolicyChangedLog(uid, oldPolicy, newPolicy));
+ mEventsBuffer.uidPolicyChanged(uid, oldPolicy, newPolicy);
+ }
+ }
+
+ void meterednessChanged(int netId, boolean newMetered) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getMeterednessChangedLog(netId, newMetered));
+ mEventsBuffer.meterednessChanged(netId, newMetered);
+ }
+ }
+
+ void removingUserState(int userId) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getUserRemovedLog(userId));
+ mEventsBuffer.userRemoved(userId);
+ }
+ }
+
+ void restrictBackgroundChanged(boolean oldValue, boolean newValue) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG,
+ getRestrictBackgroundChangedLog(oldValue, newValue));
+ mEventsBuffer.restrictBackgroundChanged(oldValue, newValue);
+ }
+ }
+
+ void deviceIdleModeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getDeviceIdleModeEnabled(enabled));
+ mEventsBuffer.deviceIdleModeEnabled(enabled);
+ }
+ }
+
+ void appIdleStateChanged(int uid, boolean idle) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getAppIdleChangedLog(uid, idle));
+ mEventsBuffer.appIdleStateChanged(uid, idle);
+ }
+ }
+
+ void paroleStateChanged(boolean paroleOn) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
+ mEventsBuffer.paroleStateChanged(paroleOn);
+ }
+ }
+
+ void tempPowerSaveWlChanged(int appId, boolean added) {
+ synchronized (mLock) {
+ if (LOGV) Slog.v(TAG, getTempPowerSaveWlChangedLog(appId, added));
+ mEventsBuffer.tempPowerSaveWlChanged(appId, added);
+ }
+ }
+
+ void uidFirewallRuleChanged(int chain, int uid, int rule) {
+ synchronized (mLock) {
+ if (LOGV) Slog.v(TAG, getUidFirewallRuleChangedLog(chain, uid, rule));
+ mEventsBuffer.uidFirewallRuleChanged(chain, uid, rule);
+ }
+ }
+
+ void firewallChainEnabled(int chain, boolean enabled) {
+ synchronized (mLock) {
+ if (LOGD) Slog.d(TAG, getFirewallChainEnabledLog(chain, enabled));
+ mEventsBuffer.firewallChainEnabled(chain, enabled);
+ }
+ }
+
+ void firewallRulesChanged(int chain, int[] uids, int[] rules) {
+ synchronized (mLock) {
+ final String log = "Firewall rules changed for " + getFirewallChainName(chain)
+ + "; uids=" + Arrays.toString(uids) + "; rules=" + Arrays.toString(rules);
+ if (LOGD) Slog.d(TAG, log);
+ mEventsBuffer.event(log);
+ }
+ }
+
+ void dumpLogs(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("mEventLogs (most recent first):");
+ pw.increaseIndent();
+ mEventsBuffer.reverseDump(pw);
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("mNetworkBlockedLogs (most recent first):");
+ pw.increaseIndent();
+ mNetworkBlockedBuffer.reverseDump(pw);
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("mUidStateChangeLogs (most recent first):");
+ pw.increaseIndent();
+ mUidStateChangeBuffer.reverseDump(pw);
+ pw.decreaseIndent();
+ }
+ }
+
+ private static String getBlockedReason(int reason) {
+ switch (reason) {
+ case NTWK_BLOCKED_POWER:
+ return "blocked by power restrictions";
+ case NTWK_ALLOWED_NON_METERED:
+ return "allowed on unmetered network";
+ case NTWK_BLOCKED_BLACKLIST:
+ return "blacklisted on metered network";
+ case NTWK_ALLOWED_WHITELIST:
+ return "whitelisted on metered network";
+ case NTWK_ALLOWED_TMP_WHITELIST:
+ return "temporary whitelisted on metered network";
+ case NTWK_BLOCKED_BG_RESTRICT:
+ return "blocked when background is restricted";
+ case NTWK_ALLOWED_DEFAULT:
+ return "allowed by default";
+ default:
+ return String.valueOf(reason);
+ }
+ }
+
+ private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) {
+ return "Policy for " + uid + " changed from "
+ + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to "
+ + NetworkPolicyManager.uidPoliciesToString(newPolicy);
+ }
+
+ private static String getMeterednessChangedLog(int netId, boolean newMetered) {
+ return "Meteredness of netId=" + netId + " changed to " + newMetered;
+ }
+
+ private static String getUserRemovedLog(int userId) {
+ return "Remove state for u" + userId;
+ }
+
+ private static String getRestrictBackgroundChangedLog(boolean oldValue, boolean newValue) {
+ return "Changed restrictBackground: " + oldValue + "->" + newValue;
+ }
+
+ private static String getDeviceIdleModeEnabled(boolean enabled) {
+ return "DeviceIdleMode enabled: " + enabled;
+ }
+
+ private static String getAppIdleChangedLog(int uid, boolean idle) {
+ return "App idle state of uid " + uid + ": " + idle;
+ }
+
+ private static String getParoleStateChanged(boolean paroleOn) {
+ return "Parole state: " + paroleOn;
+ }
+
+ private static String getTempPowerSaveWlChangedLog(int appId, boolean added) {
+ return "temp-power-save whitelist for " + appId + " changed to: " + added;
+ }
+
+ private static String getUidFirewallRuleChangedLog(int chain, int uid, int rule) {
+ return String.format("Firewall rule changed: %d-%s-%s",
+ uid, getFirewallChainName(chain), getFirewallRuleName(rule));
+ }
+
+ private static String getFirewallChainEnabledLog(int chain, boolean enabled) {
+ return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled;
+ }
+
+ private static String getFirewallChainName(int chain) {
+ switch (chain) {
+ case FIREWALL_CHAIN_DOZABLE:
+ return FIREWALL_CHAIN_NAME_DOZABLE;
+ case FIREWALL_CHAIN_STANDBY:
+ return FIREWALL_CHAIN_NAME_STANDBY;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return FIREWALL_CHAIN_NAME_POWERSAVE;
+ default:
+ return String.valueOf(chain);
+ }
+ }
+
+ private static String getFirewallRuleName(int rule) {
+ switch (rule) {
+ case FIREWALL_RULE_DEFAULT:
+ return "default";
+ case FIREWALL_RULE_ALLOW:
+ return "allow";
+ case FIREWALL_RULE_DENY:
+ return "deny";
+ default:
+ return String.valueOf(rule);
+ }
+ }
+
+ private final static class LogBuffer extends RingBuffer<Data> {
+ private static final SimpleDateFormat sFormatter
+ = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSS");
+ private static final Date sDate = new Date();
+
+ public LogBuffer(int capacity) {
+ super(Data.class, capacity);
+ }
+
+ public void uidStateChanged(int uid, int procState, long procStateSeq) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_UID_STATE_CHANGED;
+ data.ifield1 = uid;
+ data.ifield2 = procState;
+ data.lfield1 = procStateSeq;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void event(String msg) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_TYPE_GENERIC;
+ data.sfield1 = msg;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void networkBlocked(int uid, int reason) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_NETWORK_BLOCKED;
+ data.ifield1 = uid;
+ data.ifield2 = reason;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_POLICIES_CHANGED;
+ data.ifield1 = uid;
+ data.ifield2 = oldPolicy;
+ data.ifield3 = newPolicy;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void meterednessChanged(int netId, boolean newMetered) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_METEREDNESS_CHANGED;
+ data.ifield1 = netId;
+ data.bfield1 = newMetered;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void userRemoved(int userId) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_USER_STATE_REMOVED;
+ data.ifield1 = userId;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void restrictBackgroundChanged(boolean oldValue, boolean newValue) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_RESTRICT_BG_CHANGED;
+ data.bfield1 = oldValue;
+ data.bfield2 = newValue;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void deviceIdleModeEnabled(boolean enabled) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_DEVICE_IDLE_MODE_ENABLED;
+ data.bfield1 = enabled;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void appIdleStateChanged(int uid, boolean idle) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_APP_IDLE_STATE_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = idle;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void paroleStateChanged(boolean paroleOn) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_PAROLE_STATE_CHANGED;
+ data.bfield1 = paroleOn;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void tempPowerSaveWlChanged(int appId, boolean added) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_TEMP_POWER_SAVE_WL_CHANGED;
+ data.ifield1 = appId;
+ data.bfield1 = added;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void uidFirewallRuleChanged(int chain, int uid, int rule) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_UID_FIREWALL_RULE_CHANGED;
+ data.ifield1 = chain;
+ data.ifield2 = uid;
+ data.ifield3 = rule;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void firewallChainEnabled(int chain, boolean enabled) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_FIREWALL_CHAIN_ENABLED;
+ data.ifield1 = chain;
+ data.bfield1 = enabled;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void reverseDump(IndentingPrintWriter pw) {
+ final Data[] allData = toArray();
+ for (int i = allData.length - 1; i >= 0; --i) {
+ if (allData[i] == null) {
+ pw.println("NULL");
+ continue;
+ }
+ pw.print(formatDate(allData[i].timeStamp));
+ pw.print(" - ");
+ pw.println(getContent(allData[i]));
+ }
+ }
+
+ public String getContent(Data data) {
+ switch (data.type) {
+ case EVENT_TYPE_GENERIC:
+ return data.sfield1;
+ case EVENT_NETWORK_BLOCKED:
+ return data.ifield1 + "-" + getBlockedReason(data.ifield2);
+ case EVENT_UID_STATE_CHANGED:
+ return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2)
+ + "-" + data.lfield1;
+ case EVENT_POLICIES_CHANGED:
+ return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3);
+ case EVENT_METEREDNESS_CHANGED:
+ return getMeterednessChangedLog(data.ifield1, data.bfield1);
+ case EVENT_USER_STATE_REMOVED:
+ return getUserRemovedLog(data.ifield1);
+ case EVENT_RESTRICT_BG_CHANGED:
+ return getRestrictBackgroundChangedLog(data.bfield1, data.bfield2);
+ case EVENT_DEVICE_IDLE_MODE_ENABLED:
+ return getDeviceIdleModeEnabled(data.bfield1);
+ case EVENT_APP_IDLE_STATE_CHANGED:
+ return getAppIdleChangedLog(data.ifield1, data.bfield1);
+ case EVENT_PAROLE_STATE_CHANGED:
+ return getParoleStateChanged(data.bfield1);
+ case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
+ return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1);
+ case EVENT_UID_FIREWALL_RULE_CHANGED:
+ return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3);
+ case EVENT_FIREWALL_CHAIN_ENABLED:
+ return getFirewallChainEnabledLog(data.ifield1, data.bfield1);
+ default:
+ return String.valueOf(data.type);
+ }
+ }
+
+ private String formatDate(long millis) {
+ sDate.setTime(millis);
+ return sFormatter.format(sDate);
+ }
+ }
+
+ public final static class Data {
+ int type;
+ long timeStamp;
+
+ int ifield1;
+ int ifield2;
+ int ifield3;
+ long lfield1;
+ boolean bfield1;
+ boolean bfield2;
+ String sfield1;
+
+ public void reset(){
+ sfield1 = null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 3fa3cd4..fdfe241 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -82,6 +82,13 @@
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BLACKLIST;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -248,9 +255,9 @@
* </ul>
*/
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
- static final String TAG = "NetworkPolicy";
- private static final boolean LOGD = false;
- private static final boolean LOGV = false;
+ static final String TAG = NetworkPolicyLogger.TAG;
+ private static final boolean LOGD = NetworkPolicyLogger.LOGD;
+ private static final boolean LOGV = NetworkPolicyLogger.LOGV;
private static final int VERSION_INIT = 1;
private static final int VERSION_ADDED_SNOOZE = 2;
@@ -265,13 +272,6 @@
private static final int VERSION_ADDED_CYCLE = 11;
private static final int VERSION_LATEST = VERSION_ADDED_CYCLE;
- /**
- * Max items written to {@link #ProcStateSeqHistory}.
- */
- @VisibleForTesting
- public static final int MAX_PROC_STATE_SEQ_HISTORY =
- ActivityManager.isLowRamDeviceStatic() ? 50 : 200;
-
@VisibleForTesting
public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
@VisibleForTesting
@@ -471,13 +471,7 @@
private ActivityManagerInternal mActivityManagerInternal;
- /**
- * This is used for debugging purposes. Whenever the IUidObserver.onUidStateChanged is called,
- * the uid and procStateSeq will be written to this and will be printed as part of dump.
- */
- @VisibleForTesting
- public ProcStateSeqHistory mObservedHistory
- = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY);
+ private final NetworkPolicyLogger mLogger = new NetworkPolicyLogger();
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
@@ -966,6 +960,7 @@
.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
if ((oldMetered != newMetered) || mNetworkMetered.indexOfKey(network.netId) < 0) {
+ mLogger.meterednessChanged(network.netId, newMetered);
mNetworkMetered.put(network.netId, newMetered);
updateNetworkRulesNL();
}
@@ -2148,6 +2143,7 @@
final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
if (oldPolicy != policy) {
setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+ mLogger.uidPolicyChanged(uid, oldPolicy, policy);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -2168,6 +2164,7 @@
policy |= oldPolicy;
if (oldPolicy != policy) {
setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+ mLogger.uidPolicyChanged(uid, oldPolicy, policy);
}
}
}
@@ -2185,6 +2182,7 @@
policy = oldPolicy & ~policy;
if (oldPolicy != policy) {
setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+ mLogger.uidPolicyChanged(uid, oldPolicy, policy);
}
}
}
@@ -2264,7 +2262,7 @@
*/
boolean removeUserStateUL(int userId, boolean writePolicy) {
- if (LOGV) Slog.v(TAG, "removeUserStateUL()");
+ mLogger.removingUserState(userId);
boolean changed = false;
// Remove entries from revoked default restricted background UID whitelist
@@ -2429,7 +2427,6 @@
@Override
public void onTetheringChanged(String iface, boolean tethering) {
// No need to enforce permission because setRestrictBackground() will do it.
- if (LOGD) Log.d(TAG, "onTetherStateChanged(" + iface + ", " + tethering + ")");
synchronized (mUidRulesFirstLock) {
if (mRestrictBackground && tethering) {
Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver");
@@ -2486,6 +2483,7 @@
}
sendRestrictBackgroundChangedMsg();
+ mLogger.restrictBackgroundChanged(oldRestrictBackground, mRestrictBackground);
if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) {
mRestrictBackgroundChangedInBsm = true;
@@ -2551,6 +2549,7 @@
return;
}
mDeviceIdleMode = enabled;
+ mLogger.deviceIdleModeEnabled(enabled);
if (mSystemReady) {
// Device idle change means we need to rebuild rules for all
// known apps, so do a global refresh.
@@ -2964,10 +2963,7 @@
}
fout.decreaseIndent();
- fout.println("Observed uid state changes:");
- fout.increaseIndent();
- mObservedHistory.dumpUL(fout);
- fout.decreaseIndent();
+ mLogger.dumpLogs(fout);
}
}
}
@@ -3750,8 +3746,8 @@
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle);
synchronized (mUidRulesFirstLock) {
+ mLogger.appIdleStateChanged(uid, idle);
updateRuleForAppIdleUL(uid);
updateRulesForPowerRestrictionsUL(uid);
}
@@ -3762,6 +3758,7 @@
@Override
public void onParoleStateChanged(boolean isParoleOn) {
synchronized (mUidRulesFirstLock) {
+ mLogger.paroleStateChanged(isParoleOn);
updateRulesForAppIdleParoleUL();
}
}
@@ -3947,7 +3944,7 @@
synchronized (mUidRulesFirstLock) {
// We received a uid state change callback, add it to the history so that it
// will be useful for debugging.
- mObservedHistory.addProcStateSeqUL(uid, procStateSeq);
+ mLogger.uidStateChanged(uid, procState, procStateSeq);
// Now update the network policy rules as per the updated uid state.
updateUidStateUL(uid, procState);
// Updating the network rules is done, so notify AMS about this.
@@ -4081,6 +4078,7 @@
rules[index] = uidRules.valueAt(index);
}
mNetworkManager.setFirewallUidRules(chain, uids, rules);
+ mLogger.firewallRulesChanged(chain, uids, rules);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
@@ -4107,6 +4105,7 @@
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
+ mLogger.uidFirewallRuleChanged(chain, uid, rule);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
@@ -4129,6 +4128,7 @@
mFirewallChainStates.put(chain, enable);
try {
mNetworkManager.setFirewallChainEnabled(chain, enable);
+ mLogger.firewallChainEnabled(chain, enable);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem enable firewall chain", e);
} catch (RemoteException e) {
@@ -4305,30 +4305,30 @@
isBackgroundRestricted = mRestrictBackground;
}
if (hasRule(uidRules, RULE_REJECT_ALL)) {
- if (LOGV) logUidStatus(uid, "blocked by power restrictions");
+ mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
return true;
}
if (!isNetworkMetered) {
- if (LOGV) logUidStatus(uid, "allowed on unmetered network");
+ mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
return false;
}
if (hasRule(uidRules, RULE_REJECT_METERED)) {
- if (LOGV) logUidStatus(uid, "blacklisted on metered network");
+ mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
return true;
}
if (hasRule(uidRules, RULE_ALLOW_METERED)) {
- if (LOGV) logUidStatus(uid, "whitelisted on metered network");
+ mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
return false;
}
if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- if (LOGV) logUidStatus(uid, "temporary whitelisted on metered network");
+ mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
return false;
}
if (isBackgroundRestricted) {
- if (LOGV) logUidStatus(uid, "blocked when background is restricted");
+ mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
return true;
}
- if (LOGV) logUidStatus(uid, "allowed by default");
+ mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
return false;
}
@@ -4379,6 +4379,7 @@
@Override
public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
synchronized (mUidRulesFirstLock) {
+ mLogger.tempPowerSaveWlChanged(appId, added);
if (added) {
mPowerSaveTempWhitelistAppIds.put(appId, true);
} else {
@@ -4393,80 +4394,6 @@
return (uidRules & rule) != 0;
}
- private static void logUidStatus(int uid, String descr) {
- Slog.d(TAG, String.format("uid %d is %s", uid, descr));
- }
-
- /**
- * This class is used for storing and dumping the last {@link #MAX_PROC_STATE_SEQ_HISTORY}
- * (uid, procStateSeq) pairs.
- */
- @VisibleForTesting
- public static final class ProcStateSeqHistory {
- private static final int INVALID_UID = -1;
-
- /**
- * Denotes maximum number of items this history can hold.
- */
- private final int mMaxCapacity;
- /**
- * Used for storing the uid information.
- */
- private final int[] mUids;
- /**
- * Used for storing the sequence numbers associated with {@link #mUids}.
- */
- private final long[] mProcStateSeqs;
- /**
- * Points to the next available slot for writing (uid, procStateSeq) pair.
- */
- private int mHistoryNext;
-
- public ProcStateSeqHistory(int maxCapacity) {
- mMaxCapacity = maxCapacity;
- mUids = new int[mMaxCapacity];
- Arrays.fill(mUids, INVALID_UID);
- mProcStateSeqs = new long[mMaxCapacity];
- }
-
- @GuardedBy("mUidRulesFirstLock")
- public void addProcStateSeqUL(int uid, long procStateSeq) {
- mUids[mHistoryNext] = uid;
- mProcStateSeqs[mHistoryNext] = procStateSeq;
- mHistoryNext = increaseNext(mHistoryNext, 1);
- }
-
- @GuardedBy("mUidRulesFirstLock")
- public void dumpUL(IndentingPrintWriter fout) {
- if (mUids[0] == INVALID_UID) {
- fout.println("NONE");
- return;
- }
- int index = mHistoryNext;
- do {
- index = increaseNext(index, -1);
- if (mUids[index] == INVALID_UID) {
- break;
- }
- fout.println(getString(mUids[index], mProcStateSeqs[index]));
- } while (index != mHistoryNext);
- }
-
- public static String getString(int uid, long procStateSeq) {
- return "UID=" + uid + " Seq=" + procStateSeq;
- }
-
- private int increaseNext(int next, int increment) {
- next += increment;
- if (next >= mMaxCapacity) {
- next = 0;
- } else if (next < 0) {
- next = mMaxCapacity - 1;
- }
- return next;
- }
- }
-
private class NotificationId {
private final String mTag;
private final int mId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cf01400..bec6fc2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4743,7 +4743,8 @@
}
void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
- if (!mAccessibilityManager.isEnabled()) {
+ if (!mAccessibilityManager.isObservedEventType(
+ AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
return;
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index b07fe98..3792bc6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -90,6 +90,13 @@
boolean showShutdownUi(boolean isReboot, String requestString);
+ /**
+ * Show a rotation suggestion that a user may approve to rotate the screen.
+ *
+ * @param rotation rotation suggestion
+ */
+ void onProposedRotationChanged(int rotation);
+
public interface GlobalActionsListener {
/**
* Called when sysui starts and connects its status bar, or when the status bar binder
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c78a340..c7c03b4 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -406,6 +406,15 @@
}
return false;
}
+
+ @Override
+ public void onProposedRotationChanged(int rotation) {
+ if (mBar != null){
+ try {
+ mBar.onProposedRotationChanged(rotation);
+ } catch (RemoteException ex) {}
+ }
+ }
};
// ================================================================================
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 4a2da37..8b9cf4b 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -94,6 +94,7 @@
android.hardware.broadcastradio@1.2 \
android.hardware.contexthub@1.0 \
android.hardware.gnss@1.0 \
+ android.hardware.gnss@1.1 \
android.hardware.ir@1.0 \
android.hardware.light@2.0 \
android.hardware.power@1.0 \
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index daf3f2f..4cbe64d 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -18,7 +18,7 @@
#define LOG_NDEBUG 0
-#include <android/hardware/gnss/1.0/IGnss.h>
+#include <android/hardware/gnss/1.1/IGnss.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -78,6 +78,9 @@
using android::hardware::hidl_death_recipient;
using android::hidl::base::V1_0::IBase;
+using android::hardware::gnss::V1_0::GnssLocation;
+using android::hardware::gnss::V1_0::GnssLocationFlags;
+
using android::hardware::gnss::V1_0::IAGnss;
using android::hardware::gnss::V1_0::IAGnssCallback;
using android::hardware::gnss::V1_0::IAGnssCallback;
@@ -86,7 +89,6 @@
using android::hardware::gnss::V1_0::IGnss;
using android::hardware::gnss::V1_0::IGnssBatching;
using android::hardware::gnss::V1_0::IGnssBatchingCallback;
-using android::hardware::gnss::V1_0::IGnssCallback;
using android::hardware::gnss::V1_0::IGnssConfiguration;
using android::hardware::gnss::V1_0::IGnssDebug;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -100,11 +102,14 @@
using android::hardware::gnss::V1_0::IGnssXtra;
using android::hardware::gnss::V1_0::IGnssXtraCallback;
+using IGnssV1_1 = android::hardware::gnss::V1_1::IGnss;
+using android::hardware::gnss::V1_1::IGnssCallback;
+
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
// hidl_death_recipient interface
virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
- // TODO(gomo): implement a better death recovery mechanism without
+ // TODO(b/37460011): implement a better death recovery mechanism without
// crashing system server process as described in go//treble-gnss-death
LOG_ALWAYS_FATAL("Abort due to IGNSS hidl service failure,"
" restarting system server");
@@ -113,6 +118,7 @@
sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss> gnssHal = nullptr;
+sp<IGnssV1_1> gnssHalV1_1 = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil> agnssRilIface = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -303,33 +309,33 @@
return env;
}
-static jobject translateLocation(JNIEnv* env, const hardware::gnss::V1_0::GnssLocation& location) {
+static jobject translateLocation(JNIEnv* env, const GnssLocation& location) {
JavaObject object(env, "android/location/Location", "gps");
uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags);
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) {
+ if (flags & GnssLocationFlags::HAS_LAT_LONG) {
SET(Latitude, location.latitudeDegrees);
SET(Longitude, location.longitudeDegrees);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) {
+ if (flags & GnssLocationFlags::HAS_ALTITUDE) {
SET(Altitude, location.altitudeMeters);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) {
+ if (flags & GnssLocationFlags::HAS_SPEED) {
SET(Speed, location.speedMetersPerSec);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) {
+ if (flags & GnssLocationFlags::HAS_BEARING) {
SET(Bearing, location.bearingDegrees);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
+ if (flags & GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
SET(Accuracy, location.horizontalAccuracyMeters);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+ if (flags & GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
SET(VerticalAccuracyMeters, location.verticalAccuracyMeters);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) {
+ if (flags & GnssLocationFlags::HAS_SPEED_ACCURACY) {
SET(SpeedAccuracyMetersPerSecond, location.speedAccuracyMetersPerSecond);
}
- if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) {
+ if (flags & GnssLocationFlags::HAS_BEARING_ACCURACY) {
SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees);
}
SET(Time, location.timestamp);
@@ -341,8 +347,7 @@
* GnssCallback class implements the callback methods for IGnss interface.
*/
struct GnssCallback : public IGnssCallback {
- Return<void> gnssLocationCb(
- const android::hardware::gnss::V1_0::GnssLocation& location) override;
+ Return<void> gnssLocationCb(const GnssLocation& location) override;
Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override;
Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override;
Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override;
@@ -352,6 +357,9 @@
Return<void> gnssRequestTimeCb() override;
Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override;
+ // New in 1.1
+ Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
+
static GnssSvInfo sGnssSvList[static_cast<uint32_t>(
android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
static size_t sGnssSvListSize;
@@ -360,19 +368,30 @@
static size_t sNmeaStringLength;
};
+Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name) {
+ ALOGD("%s: name=%s\n", __func__, name.c_str());
+
+ // TODO(b/38003769): build Java code to connect to below code
+ /*
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareName, name);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ */
+ return Void();
+}
+
IGnssCallback::GnssSvInfo GnssCallback::sGnssSvList[static_cast<uint32_t>(
android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
const char* GnssCallback::sNmeaString = nullptr;
size_t GnssCallback::sNmeaStringLength = 0;
size_t GnssCallback::sGnssSvListSize = 0;
-Return<void> GnssCallback::gnssLocationCb(
- const ::android::hardware::gnss::V1_0::GnssLocation& location) {
+Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) {
JNIEnv* env = getJniEnv();
jobject jLocation = translateLocation(env, location);
bool hasLatLong = (static_cast<uint32_t>(location.gnssLocationFlags) &
- hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) != 0;
+ GnssLocationFlags::HAS_LAT_LONG) != 0;
env->CallVoidMethod(mCallbacksObj,
method_reportLocation,
@@ -484,12 +503,12 @@
// Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
Return<void> gnssGeofenceTransitionCb(
int32_t geofenceId,
- const android::hardware::gnss::V1_0::GnssLocation& location,
+ const GnssLocation& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) override;
Return<void> gnssGeofenceStatusCb(
GeofenceAvailability status,
- const android::hardware::gnss::V1_0::GnssLocation& location) override;
+ const GnssLocation& location) override;
Return<void> gnssGeofenceAddCb(int32_t geofenceId,
GeofenceStatus status) override;
Return<void> gnssGeofenceRemoveCb(int32_t geofenceId,
@@ -502,7 +521,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
int32_t geofenceId,
- const android::hardware::gnss::V1_0::GnssLocation& location,
+ const GnssLocation& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) {
JNIEnv* env = getJniEnv();
@@ -522,7 +541,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
GeofenceAvailability status,
- const android::hardware::gnss::V1_0::GnssLocation& location) {
+ const GnssLocation& location) {
JNIEnv* env = getJniEnv();
jobject jLocation = translateLocation(env, location);
@@ -976,12 +995,12 @@
* follow.
*/
Return<void> gnssLocationBatchCb(
- const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations)
+ const ::android::hardware::hidl_vec<GnssLocation> & locations)
override;
};
Return<void> GnssBatchingCallback::gnssLocationBatchCb(
- const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) {
+ const ::android::hardware::hidl_vec<GnssLocation> & locations) {
JNIEnv* env = getJniEnv();
jobjectArray jLocations = env->NewObjectArray(locations.size(),
@@ -1049,8 +1068,13 @@
LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
}
- // TODO(b/31632518)
- gnssHal = IGnss::getService();
+ gnssHal = gnssHalV1_1 = IGnssV1_1::getService();
+
+ if (gnssHal == nullptr) {
+ ALOGD("gnssHal 1.1 was null, trying 1.0");
+ gnssHal = IGnss::getService();
+ }
+
if (gnssHal != nullptr) {
gnssHalDeathRecipient = new GnssDeathRecipient();
hardware::Return<bool> linked = gnssHal->linkToDeath(
@@ -1160,7 +1184,6 @@
if (!mCallbacksObj)
mCallbacksObj = env->NewGlobalRef(obj);
- sp<IGnssCallback> gnssCbIface = new GnssCallback();
/*
* Fail if the main interface fails to initialize
*/
@@ -1169,7 +1192,14 @@
return JNI_FALSE;
}
- auto result = gnssHal->setCallback(gnssCbIface);
+ sp<IGnssCallback> gnssCbIface = new GnssCallback();
+
+ Return<bool> result = false;
+ if (gnssHalV1_1 != nullptr) {
+ result = gnssHalV1_1->setCallback_1_1(gnssCbIface);
+ } else {
+ result = gnssHal->setCallback(gnssCbIface);
+ }
if (!result.isOk() || !result) {
ALOGE("SetCallback for Gnss Interface fails\n");
return JNI_FALSE;
@@ -1182,7 +1212,7 @@
result = gnssXtraIface->setCallback(gnssXtraCbIface);
if (!result.isOk() || !result) {
gnssXtraIface = nullptr;
- ALOGE("SetCallback for Gnss Xtra Interface fails\n");
+ ALOGI("SetCallback for Gnss Xtra Interface fails\n");
}
}
@@ -1190,21 +1220,21 @@
if (agnssIface != nullptr) {
agnssIface->setCallback(aGnssCbIface);
} else {
- ALOGE("Unable to Initialize AGnss interface\n");
+ ALOGI("Unable to Initialize AGnss interface\n");
}
sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
if (gnssGeofencingIface != nullptr) {
gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
} else {
- ALOGE("Unable to initialize GNSS Geofencing interface\n");
+ ALOGI("Unable to initialize GNSS Geofencing interface\n");
}
sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
if (gnssNiIface != nullptr) {
gnssNiIface->setCallback(gnssNiCbIface);
} else {
- ALOGE("Unable to initialize GNSS NI interface\n");
+ ALOGI("Unable to initialize GNSS NI interface\n");
}
sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 4078829..f093d57 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -37,7 +37,6 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.Time.TIMEZONE_UTC;
-import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
@@ -61,7 +60,6 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -116,12 +114,10 @@
import android.util.TrustedTime;
import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -142,12 +138,10 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -191,8 +185,6 @@
private static final String TEST_IFACE = "test0";
private static final String TEST_SSID = "AndroidAP";
- private static final String LINE_SEPARATOR = System.getProperty("line.separator");
-
private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
/**
@@ -1109,14 +1101,6 @@
final long procStateSeq = 222;
callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq);
verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq);
-
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- final IndentingPrintWriter writer = new IndentingPrintWriter(
- new PrintWriter(outputStream), " ");
- mService.mObservedHistory.dumpUL(writer);
- writer.flush();
- assertEquals(ProcStateSeqHistory.getString(UID_A, procStateSeq),
- outputStream.toString().trim());
}
private void callOnUidStateChanged(int uid, int procState, long procStateSeq)
@@ -1129,59 +1113,6 @@
latch.await(2, TimeUnit.SECONDS);
}
- @Test
- public void testProcStateHistory() {
- // Verify dump works correctly with no elements added.
- verifyProcStateHistoryDump(0);
-
- // Add items upto half of the max capacity and verify that dump works correctly.
- verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY / 2);
-
- // Add items upto the max capacity and verify that dump works correctly.
- verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY);
-
- // Add more items than max capacity and verify that dump works correctly.
- verifyProcStateHistoryDump(MAX_PROC_STATE_SEQ_HISTORY + MAX_PROC_STATE_SEQ_HISTORY / 2);
-
- }
-
- private void verifyProcStateHistoryDump(int count) {
- final ProcStateSeqHistory history = new ProcStateSeqHistory(MAX_PROC_STATE_SEQ_HISTORY);
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- final IndentingPrintWriter writer = new IndentingPrintWriter(
- new PrintWriter(outputStream), " ");
-
- if (count == 0) {
- // Verify with no uid info written to history.
- history.dumpUL(writer);
- writer.flush();
- assertEquals("When no uid info is there, dump should contain NONE",
- "NONE", outputStream.toString().trim());
- return;
- }
-
- int uid = 111;
- long procStateSeq = 222;
- // Add count items and verify dump works correctly.
- for (int i = 0; i < count; ++i) {
- uid++;
- procStateSeq++;
- history.addProcStateSeqUL(uid, procStateSeq);
- }
- history.dumpUL(writer);
- writer.flush();
- final String[] uidsDump = outputStream.toString().split(LINE_SEPARATOR);
- // Dump will have at most MAX_PROC_STATE_SEQ_HISTORY items.
- final int expectedCount = (count < MAX_PROC_STATE_SEQ_HISTORY)
- ? count : MAX_PROC_STATE_SEQ_HISTORY;
- assertEquals(expectedCount, uidsDump.length);
- for (int i = 0; i < expectedCount; ++i) {
- assertEquals(ProcStateSeqHistory.getString(uid, procStateSeq), uidsDump[i]);
- uid--;
- procStateSeq--;
- }
- }
-
private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay,
boolean expectValid) {
config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay);
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 1adfb67..54df744 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -90,6 +90,7 @@
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private LockTaskNotify mLockTaskNotify;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Mock private RecentTasks mRecentTasks;
private LockTaskController mLockTaskController;
private Context mContext;
@@ -110,9 +111,10 @@
Looper.prepare();
}
+ mSupervisor.mRecentTasks = mRecentTasks;
+
mLockTaskController = new LockTaskController(mContext, mSupervisor,
new ImmediatelyExecuteHandler());
-
mLockTaskController.setWindowManager(mWindowManager);
mLockTaskController.mStatusBarService = mStatusBarService;
mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
@@ -601,6 +603,8 @@
eq(mContext.getPackageName()));
verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
eq(mContext.getPackageName()));
+ // THEN recents should have been notified
+ verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
// THEN the DO/PO should be informed about the operation
verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
TEST_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 6938e0f..6a1d268 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -94,12 +94,29 @@
}
@Test
- public void testStartStopTracker() {
+ public void testStartStopTrackerScreenOnOff() {
+ mInjector.mInteractive = false;
startTracker(mTracker);
- assertNotNull(mInjector.mSensorListener);
+ assertNull(mInjector.mSensorListener);
assertNotNull(mInjector.mSettingsObserver);
assertNotNull(mInjector.mBroadcastReceiver);
assertTrue(mInjector.mIdleScheduled);
+ Intent onIntent = new Intent();
+ onIntent.setAction(Intent.ACTION_SCREEN_ON);
+ mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+ onIntent);
+ assertNotNull(mInjector.mSensorListener);
+
+ Intent offIntent = new Intent();
+ offIntent.setAction(Intent.ACTION_SCREEN_OFF);
+ mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+ offIntent);
+ assertNull(mInjector.mSensorListener);
+
+ mInjector.mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(),
+ onIntent);
+ assertNotNull(mInjector.mSensorListener);
+
mTracker.stop();
assertNull(mInjector.mSensorListener);
assertNull(mInjector.mSettingsObserver);
@@ -532,7 +549,6 @@
mInjector.waitForHandler();
}
-
private static final class Idle implements MessageQueue.IdleHandler {
private boolean mIdle;
@@ -565,6 +581,7 @@
long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
Handler mHandler;
boolean mIdleScheduled;
+ boolean mInteractive = true;
public TestInjector(Handler handler) {
mHandler = handler;
@@ -577,7 +594,7 @@
@Override
public void registerSensorListener(Context context,
- SensorEventListener sensorListener) {
+ SensorEventListener sensorListener, Handler handler) {
mSensorListener = sensorListener;
}
@@ -694,5 +711,9 @@
public void cancelIdleJob(Context context) {
mIdleScheduled = false;
}
+
+ public boolean isInteractive(Context context) {
+ return mInteractive;
+ }
}
}
diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java
index 7a2344317..90a373a 100644
--- a/tests/net/java/com/android/internal/util/RingBufferTest.java
+++ b/tests/net/java/com/android/internal/util/RingBufferTest.java
@@ -17,6 +17,7 @@
package com.android.internal.util;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import android.support.test.filters.SmallTest;
@@ -129,6 +130,55 @@
assertArraysEqual(expected2, buffer.toArray());
}
+ @Test
+ public void testGetNextSlot() {
+ int capacity = 100;
+ RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+ final DummyClass1[] actual = new DummyClass1[capacity];
+ final DummyClass1[] expected = new DummyClass1[capacity];
+ for (int i = 0; i < capacity; ++i) {
+ final DummyClass1 obj = buffer.getNextSlot();
+ obj.x = capacity * i;
+ actual[i] = obj;
+ expected[i] = new DummyClass1();
+ expected[i].x = capacity * i;
+ }
+ assertArraysEqual(expected, buffer.toArray());
+
+ for (int i = 0; i < capacity; ++i) {
+ if (actual[i] != buffer.getNextSlot()) {
+ fail("getNextSlot() should re-use objects if available");
+ }
+ }
+
+ RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(No nullary constructor)", buffer2.getNextSlot());
+
+ RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(Inaccessible class)", buffer3.getNextSlot());
+ }
+
+ public static final class DummyClass1 {
+ int x;
+
+ public boolean equals(Object o) {
+ if (o instanceof DummyClass1) {
+ final DummyClass1 other = (DummyClass1) o;
+ return other.x == this.x;
+ }
+ return false;
+ }
+ }
+
+ public static final class DummyClass2 {
+ public DummyClass2(int x) {}
+ }
+
+ private static final class DummyClass3 {}
+
static <T> void assertArraysEqual(T[] expected, T[] got) {
if (expected.length != got.length) {
fail(Arrays.toString(expected) + " and " + Arrays.toString(got)