Merge "AppWidgetManager: direct add widget support."
diff --git a/api/current.txt b/api/current.txt
index 709fc12c..a84bfb5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -46139,6 +46139,11 @@
method public abstract android.view.View getFullScreenView(int, android.content.Context);
}
+ public abstract class RenderProcessGoneDetail {
+ ctor public RenderProcessGoneDetail();
+ method public abstract boolean didCrash();
+ }
+
public class ServiceWorkerClient {
ctor public ServiceWorkerClient();
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -46663,6 +46668,7 @@
method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+ method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
method public void onScaleChanged(android.webkit.WebView, float, float);
method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/removed.txt b/api/removed.txt
index 10e6eb5..35b4859 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -99,67 +99,6 @@
}
-package android.location {
-
- public final class GnssMeasurement implements android.os.Parcelable {
- field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
- }
-
- public final class GnssMeasurementsEvent implements android.os.Parcelable {
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public final class GnssNavigationMessageEvent implements android.os.Parcelable {
- ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
- method public int describeContents();
- method public android.location.GnssNavigationMessage getNavigationMessage();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public static abstract class GnssNavigationMessageEvent.Callback {
- ctor public GnssNavigationMessageEvent.Callback();
- method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
- method public void onStatusChanged(int);
- }
-
- public abstract interface GnssNmeaListener {
- method public abstract void onNmeaReceived(long, java.lang.String);
- }
-
- public final class GnssStatus {
- method public int getNumSatellites();
- method public boolean hasAlmanac(int);
- method public boolean hasEphemeris(int);
- }
-
- public abstract class GnssStatusCallback {
- ctor public GnssStatusCallback();
- method public void onFirstFix(int);
- method public void onSatelliteStatusChanged(android.location.GnssStatus);
- method public void onStarted();
- method public void onStopped();
- }
-
- public class LocationManager {
- method public boolean addNmeaListener(android.location.GnssNmeaListener);
- method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
- method public void removeNmeaListener(android.location.GnssNmeaListener);
- method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
- }
-
-}
-
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 64e18ff..aeb17fb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -26178,6 +26178,14 @@
field public static final android.os.Parcelable.Creator<android.net.RecommendationRequest> CREATOR;
}
+ public static final class RecommendationRequest.Builder {
+ ctor public RecommendationRequest.Builder();
+ method public android.net.RecommendationRequest build();
+ method public android.net.RecommendationRequest.Builder setCurrentRecommendedWifiConfig(android.net.wifi.WifiConfiguration);
+ method public android.net.RecommendationRequest.Builder setNetworkCapabilities(android.net.NetworkCapabilities);
+ method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
+ }
+
public final class RecommendationResult implements android.os.Parcelable {
method public static android.net.RecommendationResult createConnectRecommendation(android.net.wifi.WifiConfiguration);
method public static android.net.RecommendationResult createDoNotConnectRecommendation();
@@ -26242,10 +26250,16 @@
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve);
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean);
ctor public ScoredNetwork(android.net.NetworkKey, android.net.RssiCurve, boolean, android.os.Bundle);
+ method public int calculateBadge(int);
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
+ field public static final int BADGING_4K = 30; // 0x1e
+ field public static final int BADGING_HD = 20; // 0x14
+ field public static final int BADGING_NONE = 0; // 0x0
+ field public static final int BADGING_SD = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
field public final android.os.Bundle attributes;
field public final boolean meteredHint;
@@ -26253,6 +26267,9 @@
field public final android.net.RssiCurve rssiCurve;
}
+ public static abstract class ScoredNetwork.Badging implements java.lang.annotation.Annotation {
+ }
+
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
@@ -49388,6 +49405,11 @@
method public abstract android.view.View getFullScreenView(int, android.content.Context);
}
+ public abstract class RenderProcessGoneDetail {
+ ctor public RenderProcessGoneDetail();
+ method public abstract boolean didCrash();
+ }
+
public class ServiceWorkerClient {
ctor public ServiceWorkerClient();
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -49983,6 +50005,7 @@
method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+ method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
method public void onScaleChanged(android.webkit.WebView, float, float);
method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 0919b8f..8016f58 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -97,67 +97,6 @@
}
-package android.location {
-
- public final class GnssMeasurement implements android.os.Parcelable {
- field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
- }
-
- public final class GnssMeasurementsEvent implements android.os.Parcelable {
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public final class GnssNavigationMessageEvent implements android.os.Parcelable {
- ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
- method public int describeContents();
- method public android.location.GnssNavigationMessage getNavigationMessage();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public static abstract class GnssNavigationMessageEvent.Callback {
- ctor public GnssNavigationMessageEvent.Callback();
- method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
- method public void onStatusChanged(int);
- }
-
- public abstract interface GnssNmeaListener {
- method public abstract void onNmeaReceived(long, java.lang.String);
- }
-
- public final class GnssStatus {
- method public int getNumSatellites();
- method public boolean hasAlmanac(int);
- method public boolean hasEphemeris(int);
- }
-
- public abstract class GnssStatusCallback {
- ctor public GnssStatusCallback();
- method public void onFirstFix(int);
- method public void onSatelliteStatusChanged(android.location.GnssStatus);
- method public void onStarted();
- method public void onStopped();
- }
-
- public class LocationManager {
- method public boolean addNmeaListener(android.location.GnssNmeaListener);
- method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
- method public void removeNmeaListener(android.location.GnssNmeaListener);
- method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
- }
-
-}
-
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index da4ccb1..ff70fee 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -46435,6 +46435,11 @@
method public abstract android.view.View getFullScreenView(int, android.content.Context);
}
+ public abstract class RenderProcessGoneDetail {
+ ctor public RenderProcessGoneDetail();
+ method public abstract boolean didCrash();
+ }
+
public class ServiceWorkerClient {
ctor public ServiceWorkerClient();
method public android.webkit.WebResourceResponse shouldInterceptRequest(android.webkit.WebResourceRequest);
@@ -46959,6 +46964,7 @@
method public void onReceivedHttpError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceResponse);
method public void onReceivedLoginRequest(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String);
method public void onReceivedSslError(android.webkit.WebView, android.webkit.SslErrorHandler, android.net.http.SslError);
+ method public boolean onRenderProcessGone(android.webkit.WebView, android.webkit.RenderProcessGoneDetail);
method public void onScaleChanged(android.webkit.WebView, float, float);
method public deprecated void onTooManyRedirects(android.webkit.WebView, android.os.Message, android.os.Message);
method public void onUnhandledKeyEvent(android.webkit.WebView, android.view.KeyEvent);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 10e6eb5..35b4859 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -99,67 +99,6 @@
}
-package android.location {
-
- public final class GnssMeasurement implements android.os.Parcelable {
- field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
- }
-
- public final class GnssMeasurementsEvent implements android.os.Parcelable {
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public final class GnssNavigationMessageEvent implements android.os.Parcelable {
- ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
- method public int describeContents();
- method public android.location.GnssNavigationMessage getNavigationMessage();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
- field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
- field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
- field public static final int STATUS_READY = 1; // 0x1
- }
-
- public static abstract class GnssNavigationMessageEvent.Callback {
- ctor public GnssNavigationMessageEvent.Callback();
- method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
- method public void onStatusChanged(int);
- }
-
- public abstract interface GnssNmeaListener {
- method public abstract void onNmeaReceived(long, java.lang.String);
- }
-
- public final class GnssStatus {
- method public int getNumSatellites();
- method public boolean hasAlmanac(int);
- method public boolean hasEphemeris(int);
- }
-
- public abstract class GnssStatusCallback {
- ctor public GnssStatusCallback();
- method public void onFirstFix(int);
- method public void onSatelliteStatusChanged(android.location.GnssStatus);
- method public void onStarted();
- method public void onStopped();
- }
-
- public class LocationManager {
- method public boolean addNmeaListener(android.location.GnssNmeaListener);
- method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
- method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
- method public void removeNmeaListener(android.location.GnssNmeaListener);
- method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
- method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
- }
-
-}
-
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/compiled-classes-phone b/compiled-classes-phone
index f09bad9..7239a7f 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -632,6 +632,7 @@
android.bluetooth.BluetoothAudioConfig
android.bluetooth.BluetoothClass
android.bluetooth.BluetoothClass$1
+android.bluetooth.BluetoothCodecConfig
android.bluetooth.BluetoothDevice
android.bluetooth.BluetoothDevice$1
android.bluetooth.BluetoothDevice$2
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 547c710..d08bee5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -509,10 +509,23 @@
* Common-path handling of app data dir creation
*/
private static File ensurePrivateDirExists(File file) {
+ return ensurePrivateDirExists(file, 0771, -1);
+ }
+
+ private static File ensurePrivateCacheDirExists(File file) {
+ final int gid = UserHandle.getCacheAppGid(Process.myUid());
+ return ensurePrivateDirExists(file, 02771, gid);
+ }
+
+ private static File ensurePrivateDirExists(File file, int mode, int gid) {
if (!file.exists()) {
+ final String path = file.getAbsolutePath();
try {
- Os.mkdir(file.getAbsolutePath(), 0771);
- Os.chmod(file.getAbsolutePath(), 0771);
+ Os.mkdir(path, mode);
+ Os.chmod(path, mode);
+ if (gid != -1) {
+ Os.chown(path, -1, gid);
+ }
} catch (ErrnoException e) {
if (e.errno == OsConstants.EEXIST) {
// We must have raced with someone; that's okay
@@ -581,7 +594,7 @@
if (mCacheDir == null) {
mCacheDir = new File(getDataDir(), "cache");
}
- return ensurePrivateDirExists(mCacheDir);
+ return ensurePrivateCacheDirExists(mCacheDir);
}
}
@@ -591,7 +604,7 @@
if (mCodeCacheDir == null) {
mCodeCacheDir = new File(getDataDir(), "code_cache");
}
- return ensurePrivateDirExists(mCodeCacheDir);
+ return ensurePrivateCacheDirExists(mCodeCacheDir);
}
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 28ad01e..fd139f7 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -319,7 +319,12 @@
void registerUserSwitchObserver(in IUserSwitchObserver observer, in String name);
void unregisterUserSwitchObserver(in IUserSwitchObserver observer);
int[] getRunningUserIds();
+
+ // Deprecated - This method is only used by a few internal components and it will soon be
+ // replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+ // No new code should be calling it.
void requestBugReport(int bugreportType);
+
long inputDispatchingTimedOut(int pid, boolean aboveSystem, in String reason);
void clearPendingBackup();
Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 3de159a..1ce8007 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -103,6 +103,27 @@
"android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED";
/**
+ * Intent used to broadcast the change in the Audio Codec state of the
+ * A2DP Source profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li>
+ * <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
+ * connected, otherwise it is not included.</li>
+ * </ul>
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CODEC_CONFIG_CHANGED =
+ "android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED";
+
+ /**
* A2DP sink device is streaming music. This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
* {@link #ACTION_PLAYING_STATE_CHANGED} intent.
@@ -544,6 +565,54 @@
}
/**
+ * Gets the current codec configuration.
+ *
+ * @return the current codec configuration
+ * @hide
+ */
+ public BluetoothCodecConfig getCodecConfig() {
+ if (DBG) Log.d(TAG, "getCodecConfig");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ return mService.getCodecConfig();
+ }
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+ return null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in getCodecConfig()", e);
+ return null;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Sets the codec configuration preference.
+ *
+ * @param codecConfig the codec configuration preference
+ * @hide
+ */
+ public void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
+ if (DBG) Log.d(TAG, "setCodecConfigPreference");
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null && isEnabled()) {
+ mService.setCodecConfigPreference(codecConfig);
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
+ return;
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/location/java/android/location/GnssNavigationMessageEvent.aidl b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
similarity index 74%
rename from location/java/android/location/GnssNavigationMessageEvent.aidl
rename to core/java/android/bluetooth/BluetoothCodecConfig.aidl
index e765739..553e66e 100644
--- a/location/java/android/location/GnssNavigationMessageEvent.aidl
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.aidl
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2014, The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.location;
+package android.bluetooth;
-parcelable GnssNavigationMessageEvent;
+parcelable BluetoothCodecConfig;
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
new file mode 100644
index 0000000..5cc1277
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents the codec configuration for a Bluetooth A2DP source device.
+ *
+ * {@see BluetoothA2dp}
+ *
+ * {@hide}
+ */
+public final class BluetoothCodecConfig implements Parcelable {
+
+ /**
+ * Extra for the codec configuration intents of the individual profiles.
+ *
+ * This extra represents the current codec configuration of the A2DP
+ * profile.
+ */
+ public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG";
+
+ /**
+ * Extra for the codec configuration intents of the individual profiles.
+ *
+ * This extra represents the previous codec configuration of the A2DP
+ * profile.
+ */
+ public static final String EXTRA_PREVIOUS_CODEC_CONFIG =
+ "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG";
+
+ public static final int SOURCE_CODEC_TYPE_SBC = 0;
+ public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
+
+ public static final int CODEC_PRIORITY_DEFAULT = 0;
+ public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
+
+ public static final int SAMPLE_RATE_NONE = 0;
+ public static final int SAMPLE_RATE_44100 = 0x1 << 0;
+ public static final int SAMPLE_RATE_48000 = 0x1 << 1;
+ public static final int SAMPLE_RATE_88200 = 0x1 << 2;
+ public static final int SAMPLE_RATE_96000 = 0x1 << 3;
+ public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+ public static final int SAMPLE_RATE_192000 = 0x1 << 5;
+
+ public static final int BITS_PER_SAMPLE_NONE = 0;
+ public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
+ public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
+ public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
+
+ public static final int CHANNEL_MODE_NONE = 0;
+ public static final int CHANNEL_MODE_MONO = 0x1 << 0;
+ public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
+
+ private final int mCodecType;
+ private final int mCodecPriority;
+ private final int mSampleRate;
+ private final int mBitsPerSample;
+ private final int mChannelMode;
+ private final long mCodecSpecific1;
+ private final long mCodecSpecific2;
+ private final long mCodecSpecific3;
+ private final long mCodecSpecific4;
+
+ public BluetoothCodecConfig(int codecType, int codecPriority,
+ int sampleRate, int bitsPerSample,
+ int channelMode,long codecSpecific1,
+ long codecSpecific2, long codecSpecific3,
+ long codecSpecific4) {
+ mCodecType = codecType;
+ mCodecPriority = codecPriority;
+ mSampleRate = sampleRate;
+ mBitsPerSample = bitsPerSample;
+ mChannelMode = channelMode;
+ mCodecSpecific1 = codecSpecific1;
+ mCodecSpecific2 = codecSpecific2;
+ mCodecSpecific3 = codecSpecific3;
+ mCodecSpecific4 = codecSpecific4;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothCodecConfig) {
+ BluetoothCodecConfig other = (BluetoothCodecConfig)o;
+ return (other.mCodecType == mCodecType &&
+ other.mCodecPriority == mCodecPriority &&
+ other.mSampleRate == mSampleRate &&
+ other.mBitsPerSample == mBitsPerSample &&
+ other.mChannelMode == mChannelMode &&
+ other.mCodecSpecific1 == mCodecSpecific1 &&
+ other.mCodecSpecific2 == mCodecSpecific2 &&
+ other.mCodecSpecific3 == mCodecSpecific3 &&
+ other.mCodecSpecific4 == mCodecSpecific4);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
+ mBitsPerSample, mChannelMode, mCodecSpecific1,
+ mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
+ }
+
+ @Override
+ public String toString() {
+ return "{mCodecType:" + mCodecType +
+ ",mCodecPriority:" + mCodecPriority +
+ ",mSampleRate:" + String.format("0x%x", mSampleRate) +
+ ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) +
+ ",mChannelMode:" + String.format("0x%x", mChannelMode) +
+ ",mCodecSpecific1:" + mCodecSpecific1 +
+ ",mCodecSpecific2:" + mCodecSpecific2 +
+ ",mCodecSpecific3:" + mCodecSpecific3 +
+ ",mCodecSpecific4:" + mCodecSpecific4 + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothCodecConfig> CREATOR =
+ new Parcelable.Creator<BluetoothCodecConfig>() {
+ public BluetoothCodecConfig createFromParcel(Parcel in) {
+ final int codecType = in.readInt();
+ final int codecPriority = in.readInt();
+ final int sampleRate = in.readInt();
+ final int bitsPerSample = in.readInt();
+ final int channelMode = in.readInt();
+ final long codecSpecific1 = in.readLong();
+ final long codecSpecific2 = in.readLong();
+ final long codecSpecific3 = in.readLong();
+ final long codecSpecific4 = in.readLong();
+ return new BluetoothCodecConfig(codecType, codecPriority,
+ sampleRate, bitsPerSample,
+ channelMode, codecSpecific1,
+ codecSpecific2, codecSpecific3,
+ codecSpecific4);
+ }
+ public BluetoothCodecConfig[] newArray(int size) {
+ return new BluetoothCodecConfig[size];
+ }
+ };
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mCodecType);
+ out.writeInt(mCodecPriority);
+ out.writeInt(mSampleRate);
+ out.writeInt(mBitsPerSample);
+ out.writeInt(mChannelMode);
+ out.writeLong(mCodecSpecific1);
+ out.writeLong(mCodecSpecific2);
+ out.writeLong(mCodecSpecific3);
+ out.writeLong(mCodecSpecific4);
+ }
+
+ /**
+ * Returns the codec type.
+ * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
+ *
+ * @return the codec type
+ */
+ public int getCodecType() {
+ return mCodecType;
+ }
+
+ /**
+ * Returns the codec selection priority.
+ * The codec selection priority is relative to other codecs: larger value
+ * means higher priority. If 0, reset to default.
+ *
+ * @return the codec priority
+ */
+ public int getCodecPriority() {
+ return mCodecPriority;
+ }
+
+ /**
+ * Returns the codec sample rate. The value can be a bitmask with all
+ * supported sample rates:
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or
+ * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000}
+ *
+ * @return the codec sample rate
+ */
+ public int getSampleRate() {
+ return mSampleRate;
+ }
+
+ /**
+ * Returns the codec bits per sample. The value can be a bitmask with all
+ * bits per sample supported:
+ * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
+ * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
+ * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or
+ * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32}
+ *
+ * @return the codec bits per sample
+ */
+ public int getBitsPerSample() {
+ return mBitsPerSample;
+ }
+
+ /**
+ * Returns the codec channel mode. The value can be a bitmask with all
+ * supported channel modes:
+ * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
+ * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
+ * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO}
+ *
+ * @return the codec channel mode
+ */
+ public int getChannelMode() {
+ return mChannelMode;
+ }
+
+ /**
+ * Returns a codec specific value1.
+ *
+ * @return a codec specific value1.
+ */
+ public long getCodecSpecific1() {
+ return mCodecSpecific1;
+ }
+
+ /**
+ * Returns a codec specific value2.
+ *
+ * @return a codec specific value2
+ */
+ public long getCodecSpecific2() {
+ return mCodecSpecific2;
+ }
+
+ /**
+ * Returns a codec specific value3.
+ *
+ * @return a codec specific value3
+ */
+ public long getCodecSpecific3() {
+ return mCodecSpecific3;
+ }
+
+ /**
+ * Returns a codec specific value4.
+ *
+ * @return a codec specific value4
+ */
+ public long getCodecSpecific4() {
+ return mCodecSpecific4;
+ }
+
+ /**
+ * Checks whether the audio feeding parameters are same.
+ *
+ * @param other the codec config to compare against
+ * @return true if the audio feeding parameters are same, otherwise false
+ */
+ public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
+ return (other != null && other.mSampleRate == mSampleRate &&
+ other.mBitsPerSample == mBitsPerSample &&
+ other.mChannelMode == mChannelMode);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 0f0e050..f519776 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -45,9 +45,9 @@
* <code>false</code> otherwise.
*/
public void onAppStatusChanged(BluetoothDevice pluggedDevice,
- BluetoothHidDeviceAppConfiguration config, boolean registered) {
- Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + (pluggedDevice == null ?
- null : pluggedDevice.toString()) + " registered=" + registered);
+ BluetoothHidDeviceAppConfiguration config, boolean registered) {
+ Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
+ + registered);
}
/**
@@ -60,13 +60,13 @@
* @param state Connection state as defined in {@link BluetoothProfile}.
*/
public void onConnectionStateChanged(BluetoothDevice device, int state) {
- Log.d(TAG, "onConnectionStateChanged: device=" + device.toString() + " state=" + state);
+ Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
}
/**
* Callback called when GET_REPORT is received from remote host. Should be
* replied by application using
- * {@link BluetoothHidDevice#replyReport(byte, byte, byte[])}.
+ * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}.
*
* @param type Requested Report Type.
* @param id Requested Report Id, can be 0 if no Report Id are defined in
@@ -74,21 +74,22 @@
* @param bufferSize Requested buffer size, application shall respond with
* at least given number of bytes.
*/
- public void onGetReport(byte type, byte id, int bufferSize) {
- Log.d(TAG, "onGetReport: type=" + type + " id=" + id + " bufferSize=" + bufferSize);
+ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+ Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
+ + bufferSize);
}
/**
* Callback called when SET_REPORT is received from remote host. In case
* received data are invalid, application shall respond with
- * {@link BluetoothHidDevice#reportError()}.
+ * {@link BluetoothHidDevice#reportError(BluetoothDevice)}.
*
* @param type Report Type.
* @param id Report Id.
* @param data Report data.
*/
- public void onSetReport(byte type, byte id, byte[] data) {
- Log.d(TAG, "onSetReport: type=" + type + " id=" + id);
+ public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+ Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
}
/**
@@ -99,8 +100,8 @@
*
* @param protocol Protocol Mode.
*/
- public void onSetProtocol(byte protocol) {
- Log.d(TAG, "onSetProtocol: protocol=" + protocol);
+ public void onSetProtocol(BluetoothDevice device, byte protocol) {
+ Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
}
/**
@@ -111,16 +112,17 @@
* @param reportId Report Id.
* @param data Report data.
*/
- public void onIntrData(byte reportId, byte[] data) {
- Log.d(TAG, "onIntrData: reportId=" + reportId);
+ public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+ Log.d(TAG, "onIntrData: device=" + device + " reportId=" + reportId);
}
/**
* Callback called when Virtual Cable is removed. This can be either due to
- * {@link BluetoothHidDevice#unplug()} or request from remote side. After
- * this callback is received connection will be disconnected automatically.
+ * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote
+ * side. After this callback is received connection will be disconnected
+ * automatically.
*/
- public void onVirtualCableUnplug() {
- Log.d(TAG, "onVirtualCableUnplug");
+ public void onVirtualCableUnplug(BluetoothDevice device) {
+ Log.d(TAG, "onVirtualCableUnplug: device=" + device);
}
}
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
index 129fe7e..68d105f 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -27,6 +27,7 @@
import android.util.Log;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -137,28 +138,28 @@
}
@Override
- public void onGetReport(byte type, byte id, int bufferSize) {
- mCallback.onGetReport(type, id, bufferSize);
+ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+ mCallback.onGetReport(device, type, id, bufferSize);
}
@Override
- public void onSetReport(byte type, byte id, byte[] data) {
- mCallback.onSetReport(type, id, data);
+ public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+ mCallback.onSetReport(device, type, id, data);
}
@Override
- public void onSetProtocol(byte protocol) {
- mCallback.onSetProtocol(protocol);
+ public void onSetProtocol(BluetoothDevice device, byte protocol) {
+ mCallback.onSetProtocol(device, protocol);
}
@Override
- public void onIntrData(byte reportId, byte[] data) {
- mCallback.onIntrData(reportId, data);
+ public void onIntrData(BluetoothDevice device, byte reportId, byte[] data) {
+ mCallback.onIntrData(device, reportId, data);
}
@Override
- public void onVirtualCableUnplug() {
- mCallback.onVirtualCableUnplug();
+ public void onVirtualCableUnplug(BluetoothDevice device) {
+ mCallback.onVirtualCableUnplug(device);
}
}
@@ -276,21 +277,59 @@
mServiceListener = null;
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public List<BluetoothDevice> getConnectedDevices() {
Log.v(TAG, "getConnectedDevices()");
- return null;
+
+ if (mService != null) {
+ try {
+ return mService.getConnectedDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return new ArrayList<BluetoothDevice>();
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
- return null;
+
+ if (mService != null) {
+ try {
+ return mService.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
+
+ return new ArrayList<BluetoothDevice>();
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public int getConnectionState(BluetoothDevice device) {
- Log.v(TAG, "getConnectionState(): device=" + device.getAddress());
+ Log.v(TAG, "getConnectionState(): device=" + device);
+
+ if (mService != null) {
+ try {
+ return mService.getConnectionState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ }
return STATE_DISCONNECTED;
}
@@ -379,14 +418,12 @@
* @param data Report data, not including Report Id.
* @return
*/
- public boolean sendReport(int id, byte[] data) {
- Log.v(TAG, "sendReport(): id=" + id);
-
+ public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
if (mService != null) {
try {
- result = mService.sendReport(id, data);
+ result = mService.sendReport(device, id, data);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -399,21 +436,21 @@
/**
* Sends report to remote host as reply for GET_REPORT request from
- * {@link BluetoothHidDeviceCallback#onGetReport(byte, byte, int)}.
+ * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
*
* @param type Report Type, as in request.
* @param id Report Id, as in request.
* @param data Report data, not including Report Id.
* @return
*/
- public boolean replyReport(byte type, byte id, byte[] data) {
- Log.v(TAG, "replyReport(): type=" + type + " id=" + id);
+ public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+ Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
boolean result = false;
if (mService != null) {
try {
- result = mService.replyReport(type, id, data);
+ result = mService.replyReport(device, type, id, data);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -426,19 +463,19 @@
/**
* Sends error handshake message as reply for invalid SET_REPORT request
- * from {@link BluetoothHidDeviceCallback#onSetReport(byte, byte, byte[])}.
+ * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
*
* @param error Error to be sent for SET_REPORT via HANDSHAKE.
* @return
*/
- public boolean reportError(byte error) {
- Log.v(TAG, "reportError(): error = " + error);
+ public boolean reportError(BluetoothDevice device, byte error) {
+ Log.v(TAG, "reportError(): device=" + device + " error=" + error);
boolean result = false;
if (mService != null) {
try {
- result = mService.reportError(error);
+ result = mService.reportError(device, error);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -454,14 +491,14 @@
*
* @return
*/
- public boolean unplug() {
- Log.v(TAG, "unplug()");
+ public boolean unplug(BluetoothDevice device) {
+ Log.v(TAG, "unplug(): device=" + device);
boolean result = false;
if (mService != null) {
try {
- result = mService.unplug();
+ result = mService.unplug(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -478,14 +515,14 @@
*
* @return
*/
- public boolean connect() {
- Log.v(TAG, "connect()");
+ public boolean connect(BluetoothDevice device) {
+ Log.v(TAG, "connect(): device=" + device);
boolean result = false;
if (mService != null) {
try {
- result = mService.connect();
+ result = mService.connect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -501,14 +538,14 @@
*
* @return
*/
- public boolean disconnect() {
- Log.v(TAG, "disconnect()");
+ public boolean disconnect(BluetoothDevice device) {
+ Log.v(TAG, "disconnect(): device=" + device);
boolean result = false;
if (mService != null) {
try {
- result = mService.disconnect();
+ result = mService.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 26ff9e27..5b524eb 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
/**
@@ -36,4 +37,6 @@
oneway void adjustAvrcpAbsoluteVolume(int direction);
oneway void setAvrcpAbsoluteVolume(int volume);
boolean isA2dpPlaying(in BluetoothDevice device);
+ BluetoothCodecConfig getCodecConfig();
+ oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
}
diff --git a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
index 1252876..a737198 100644
--- a/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl
@@ -23,9 +23,9 @@
interface IBluetoothHidDeviceCallback {
void onAppStatusChanged(in BluetoothDevice device, in BluetoothHidDeviceAppConfiguration config, boolean registered);
void onConnectionStateChanged(in BluetoothDevice device, in int state);
- void onGetReport(in byte type, in byte id, in int bufferSize);
- void onSetReport(in byte type, in byte id, in byte[] data);
- void onSetProtocol(in byte protocol);
- void onIntrData(in byte reportId, in byte[] data);
- void onVirtualCableUnplug();
+ void onGetReport(in BluetoothDevice device, in byte type, in byte id, in int bufferSize);
+ void onSetReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+ void onSetProtocol(in BluetoothDevice device, in byte protocol);
+ void onIntrData(in BluetoothDevice device, in byte reportId, in byte[] data);
+ void onVirtualCableUnplug(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothInputHost.aidl b/core/java/android/bluetooth/IBluetoothInputHost.aidl
index b2c421c..6c4993f 100644
--- a/core/java/android/bluetooth/IBluetoothInputHost.aidl
+++ b/core/java/android/bluetooth/IBluetoothInputHost.aidl
@@ -28,10 +28,13 @@
in BluetoothHidDeviceAppSdpSettings sdp, in BluetoothHidDeviceAppQosSettings inQos,
in BluetoothHidDeviceAppQosSettings outQos, in IBluetoothHidDeviceCallback callback);
boolean unregisterApp(in BluetoothHidDeviceAppConfiguration config);
- boolean sendReport(in int id, in byte[] data);
- boolean replyReport(in byte type, in byte id, in byte[] data);
- boolean reportError(byte error);
- boolean unplug();
- boolean connect();
- boolean disconnect();
+ boolean sendReport(in BluetoothDevice device, in int id, in byte[] data);
+ boolean replyReport(in BluetoothDevice device, in byte type, in byte id, in byte[] data);
+ boolean reportError(in BluetoothDevice device, byte error);
+ boolean unplug(in BluetoothDevice device);
+ boolean connect(in BluetoothDevice device);
+ boolean disconnect(in BluetoothDevice device);
+ List<BluetoothDevice> getConnectedDevices();
+ List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+ int getConnectionState(in BluetoothDevice device);
}
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 9464222..e1eaf00 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -87,9 +87,9 @@
{
LocalSocketImpl acceptedImpl = new LocalSocketImpl();
- impl.accept (acceptedImpl);
+ impl.accept(acceptedImpl);
- return new LocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
+ return LocalSocket.createLocalSocketForAccept(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
}
/**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index e14facb1..3f8d9d3 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -75,17 +75,24 @@
isConnected = true;
}
- /**
- * for use with AndroidServerSocket
- * @param impl a SocketImpl
- */
- /*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
+ private LocalSocket(LocalSocketImpl impl, int sockType) {
this.impl = impl;
this.sockType = sockType;
this.isConnected = false;
this.isBound = false;
}
+ /**
+ * for use with LocalServerSocket.accept()
+ */
+ static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl, int sockType) {
+ LocalSocket socket = new LocalSocket(impl, sockType);
+ socket.isConnected = true;
+ socket.isBound = true;
+ socket.implCreated = true;
+ return socket;
+ }
+
/** {@inheritDoc} */
@Override
public String toString() {
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 0f0e9c4..d8f7821 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -235,29 +235,29 @@
* @throws IOException
*/
public void create(int sockType) throws IOException {
- // no error if socket already created
- // need this for LocalServerSocket.accept()
- if (fd == null) {
- int osType;
- switch (sockType) {
- case LocalSocket.SOCKET_DGRAM:
- osType = OsConstants.SOCK_DGRAM;
- break;
- case LocalSocket.SOCKET_STREAM:
- osType = OsConstants.SOCK_STREAM;
- break;
- case LocalSocket.SOCKET_SEQPACKET:
- osType = OsConstants.SOCK_SEQPACKET;
- break;
- default:
- throw new IllegalStateException("unknown sockType");
- }
- try {
- fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
- mFdCreatedInternally = true;
- } catch (ErrnoException e) {
- e.rethrowAsIOException();
- }
+ if (fd != null) {
+ throw new IOException("LocalSocketImpl already has an fd");
+ }
+
+ int osType;
+ switch (sockType) {
+ case LocalSocket.SOCKET_DGRAM:
+ osType = OsConstants.SOCK_DGRAM;
+ break;
+ case LocalSocket.SOCKET_STREAM:
+ osType = OsConstants.SOCK_STREAM;
+ break;
+ case LocalSocket.SOCKET_SEQPACKET:
+ osType = OsConstants.SOCK_SEQPACKET;
+ break;
+ default:
+ throw new IllegalStateException("unknown sockType");
+ }
+ try {
+ fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
+ mFdCreatedInternally = true;
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
}
}
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index a96f90d..d013f64 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -41,6 +41,7 @@
* Builder class for constructing {@link RecommendationRequest} instances.
* @hide
*/
+ @SystemApi
public static final class Builder {
private ScanResult[] mScanResults;
private WifiConfiguration mCurrentConfig;
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index 94e5187..7e3dd77 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Bundle;
@@ -24,6 +25,8 @@
import java.lang.Math;
import java.lang.UnsupportedOperationException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -33,6 +36,15 @@
*/
@SystemApi
public class ScoredNetwork implements Parcelable {
+
+ /**
+ * Key used with the {@link #attributes} bundle to define the badging curve.
+ *
+ * <p>The badging curve is a {@link RssiCurve} used to map different RSSI values to {@link
+ * Badging} enums.
+ */
+ public static final String ATTRIBUTES_KEY_BADGING_CURVE =
+ "android.net.attributes.key.BADGING_CURVE";
/**
* Extra used with {@link #attributes} to specify whether the
* network is believed to have a captive portal.
@@ -58,6 +70,15 @@
public static final String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET =
"android.net.attributes.key.RANKING_SCORE_OFFSET";
+ @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Badging {}
+
+ public static final int BADGING_NONE = 0;
+ public static final int BADGING_SD = 10;
+ public static final int BADGING_HD = 20;
+ public static final int BADGING_4K = 30;
+
/** A {@link NetworkKey} uniquely identifying this network. */
public final NetworkKey networkKey;
@@ -249,6 +270,25 @@
}
}
+ /**
+ * Return the {@link Badging} enum for this network for the given RSSI, derived from the
+ * badging curve.
+ *
+ * <p>If no badging curve is present, {@link #BADGE_NONE} will be returned.
+ *
+ * @param rssi The rssi level for which the badge should be calculated
+ */
+ @Badging
+ public int calculateBadge(int rssi) {
+ if (attributes != null && attributes.containsKey(ATTRIBUTES_KEY_BADGING_CURVE)) {
+ RssiCurve badgingCurve =
+ attributes.getParcelable(ATTRIBUTES_KEY_BADGING_CURVE);
+ return badgingCurve.lookupScore(rssi);
+ }
+
+ return BADGING_NONE;
+ }
+
public static final Parcelable.Creator<ScoredNetwork> CREATOR =
new Parcelable.Creator<ScoredNetwork>() {
@Override
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0d6d369..daf8b2d 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -818,7 +818,7 @@
*/
public static boolean isBuildConsistent() {
// Don't care on eng builds. Incremental build may trigger false negative.
- if ("eng".equals(TYPE)) return true;
+ if (IS_ENG) return true;
final String system = SystemProperties.get("ro.build.fingerprint");
final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
@@ -882,6 +882,10 @@
public static final boolean IS_DEBUGGABLE =
SystemProperties.getInt("ro.debuggable", 0) == 1;
+ /** {@hide} */
+ public static final boolean IS_ENG =
+ "eng".equals(getString("ro.build.type"));
+
/**
* Specifies whether the permissions needed by a legacy app should be
* reviewed before any of its components can run. A legacy app is one
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 481b2dc..bcc3468 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -33,11 +33,14 @@
mNativeContext);
}
+ @Override
public final native void transact(
- int code, HwParcel request, HwParcel reply, int flags);
+ int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException;
public abstract void onTransact(
- int code, HwParcel request, HwParcel reply, int flags);
+ int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException;
public native final void registerService(
ArrayList<String> interfaceChain,
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index e617e0a..2f89ce6 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -32,12 +32,15 @@
mNativeContext);
}
+ @Override
public IHwInterface queryLocalInterface(String descriptor) {
return null;
}
+ @Override
public native final void transact(
- int code, HwParcel request, HwParcel reply, int flags);
+ int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException;
public native boolean linkToDeath(DeathRecipient recipient, long cookie);
public native boolean unlinkToDeath(DeathRecipient recipient);
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index f93bfd7..2a65679 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -23,7 +23,8 @@
public static final int FLAG_ONEWAY = 1;
public void transact(
- int code, HwParcel request, HwParcel reply, int flags);
+ int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException;
public IHwInterface queryLocalInterface(String descriptor);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 9cd1a42..d6688e3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -184,6 +184,11 @@
*/
public static final int LAST_SHARED_APPLICATION_GID = 59999;
+ /** {@hide} */
+ public static final int FIRST_APPLICATION_CACHE_GID = 20000;
+ /** {@hide} */
+ public static final int LAST_APPLICATION_CACHE_GID = 29999;
+
/**
* Standard priority of application threads.
* Use with {@link #setThreadPriority(int)} and
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b04d149..4aec8ae 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -215,6 +215,15 @@
}
/**
+ * Returns the cache GID for a given UID or appId.
+ * @hide
+ */
+ public static int getCacheAppGid(int id) {
+ return Process.FIRST_APPLICATION_CACHE_GID + (id % PER_USER_RANGE)
+ - Process.FIRST_APPLICATION_UID;
+ }
+
+ /**
* Generate a text representation of the uid, breaking out its individual
* components -- user, app, isolated, etc.
* @hide
diff --git a/core/java/android/speech/tts/EventLoggerV1.java b/core/java/android/speech/tts/EventLogger.java
similarity index 87%
rename from core/java/android/speech/tts/EventLoggerV1.java
rename to core/java/android/speech/tts/EventLogger.java
index 2b02b43..f03df31 100644
--- a/core/java/android/speech/tts/EventLoggerV1.java
+++ b/core/java/android/speech/tts/EventLogger.java
@@ -18,14 +18,14 @@
import android.text.TextUtils;
/**
- * Writes data about a given speech synthesis request for V1 API to the event
- * logs. The data that is logged includes the calling app, length of the
- * utterance, speech rate / pitch, the latency, and overall time taken.
+ * Writes data about a given speech synthesis request to the event logs. The data that is logged
+ * includes the calling app, length of the utterance, speech rate / pitch, the latency, and overall
+ * time taken.
*/
-class EventLoggerV1 extends AbstractEventLogger {
+class EventLogger extends AbstractEventLogger {
private final SynthesisRequest mRequest;
- EventLoggerV1(SynthesisRequest request, int callerUid, int callerPid, String serviceApp) {
+ EventLogger(SynthesisRequest request, int callerUid, int callerPid, String serviceApp) {
super(callerUid, callerPid, serviceApp);
mRequest = request;
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 171a72c..55da52b 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -226,17 +226,14 @@
protected abstract void onStop();
/**
- * Tells the service to synthesize speech from the given text. This method
- * should block until the synthesis is finished. Used for requests from V1
- * clients ({@link android.speech.tts.TextToSpeech}). Called on the synthesis
- * thread.
+ * Tells the service to synthesize speech from the given text. This method should block until
+ * the synthesis is finished. Called on the synthesis thread.
*
* @param request The synthesis request.
- * @param callback The callback that the engine must use to make data
- * available for playback or for writing to a file.
+ * @param callback The callback that the engine must use to make data available for playback or
+ * for writing to a file.
*/
- protected abstract void onSynthesizeText(SynthesisRequest request,
- SynthesisCallback callback);
+ protected abstract void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback);
/**
* Queries the service for a set of features supported for a given language.
@@ -713,8 +710,7 @@
}
/** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */
- static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle,
- boolean isSpeech) {
+ static AudioOutputParams createFromParamsBundle(Bundle paramsBundle, boolean isSpeech) {
if (paramsBundle == null) {
return new AudioOutputParams();
}
@@ -902,16 +898,19 @@
}
/**
- * UtteranceSpeechItem for V1 API speech items. V1 API speech items keep
- * synthesis parameters in a single Bundle passed as parameter. This class
- * allow subclasses to access them conveniently.
+ * Synthesis parameters are kept in a single Bundle passed as parameter. This class allow
+ * subclasses to access them conveniently.
*/
- private abstract class SpeechItemV1 extends UtteranceSpeechItem {
+ private abstract class UtteranceSpeechItemWithParams extends UtteranceSpeechItem {
protected final Bundle mParams;
protected final String mUtteranceId;
- SpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params, String utteranceId) {
+ UtteranceSpeechItemWithParams(
+ Object callerIdentity,
+ int callerUid,
+ int callerPid,
+ Bundle params,
+ String utteranceId) {
super(callerIdentity, callerUid, callerPid);
mParams = params;
mUtteranceId = utteranceId;
@@ -935,11 +934,11 @@
}
AudioOutputParams getAudioParams() {
- return AudioOutputParams.createFromV1ParamsBundle(mParams, true);
+ return AudioOutputParams.createFromParamsBundle(mParams, true);
}
}
- class SynthesisSpeechItemV1 extends SpeechItemV1 {
+ class SynthesisSpeechItem extends UtteranceSpeechItemWithParams {
// Never null.
private final CharSequence mText;
private final SynthesisRequest mSynthesisRequest;
@@ -947,19 +946,23 @@
// Non null after synthesis has started, and all accesses
// guarded by 'this'.
private AbstractSynthesisCallback mSynthesisCallback;
- private final EventLoggerV1 mEventLogger;
+ private final EventLogger mEventLogger;
private final int mCallerUid;
- public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params, String utteranceId, CharSequence text) {
+ public SynthesisSpeechItem(
+ Object callerIdentity,
+ int callerUid,
+ int callerPid,
+ Bundle params,
+ String utteranceId,
+ CharSequence text) {
super(callerIdentity, callerUid, callerPid, params, utteranceId);
mText = text;
mCallerUid = callerUid;
mSynthesisRequest = new SynthesisRequest(mText, mParams);
mDefaultLocale = getSettingsLocale();
setRequestParams(mSynthesisRequest);
- mEventLogger = new EventLoggerV1(mSynthesisRequest, callerUid, callerPid,
- mPackageName);
+ mEventLogger = new EventLogger(mSynthesisRequest, callerUid, callerPid, mPackageName);
}
public CharSequence getText() {
@@ -1053,11 +1056,16 @@
}
}
- private class SynthesisToFileOutputStreamSpeechItemV1 extends SynthesisSpeechItemV1 {
+ private class SynthesisToFileOutputStreamSpeechItem extends SynthesisSpeechItem {
private final FileOutputStream mFileOutputStream;
- public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid,
- int callerPid, Bundle params, String utteranceId, CharSequence text,
+ public SynthesisToFileOutputStreamSpeechItem(
+ Object callerIdentity,
+ int callerUid,
+ int callerPid,
+ Bundle params,
+ String utteranceId,
+ CharSequence text,
FileOutputStream fileOutputStream) {
super(callerIdentity, callerUid, callerPid, params, utteranceId, text);
mFileOutputStream = fileOutputStream;
@@ -1080,11 +1088,16 @@
}
}
- private class AudioSpeechItemV1 extends SpeechItemV1 {
+ private class AudioSpeechItem extends UtteranceSpeechItemWithParams {
private final AudioPlaybackQueueItem mItem;
- public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid,
- Bundle params, String utteranceId, Uri uri) {
+ public AudioSpeechItem(
+ Object callerIdentity,
+ int callerUid,
+ int callerPid,
+ Bundle params,
+ String utteranceId,
+ Uri uri) {
super(callerIdentity, callerUid, callerPid, params, utteranceId);
mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
TextToSpeechService.this, uri, getAudioParams());
@@ -1112,7 +1125,7 @@
@Override
AudioOutputParams getAudioParams() {
- return AudioOutputParams.createFromV1ParamsBundle(mParams, false);
+ return AudioOutputParams.createFromParamsBundle(mParams, false);
}
}
@@ -1219,202 +1232,252 @@
}
/**
- * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
- * called called from several different threads.
+ * Binder returned from {@code #onBind(Intent)}. The methods in this class can be called called
+ * from several different threads.
*/
// NOTE: All calls that are passed in a calling app are interned so that
// they can be used as message objects (which are tested for equality using ==).
- private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
- @Override
- public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params,
- String utteranceId) {
- if (!checkNonNull(caller, text, params)) {
- return TextToSpeech.ERROR;
- }
+ private final ITextToSpeechService.Stub mBinder =
+ new ITextToSpeechService.Stub() {
+ @Override
+ public int speak(
+ IBinder caller,
+ CharSequence text,
+ int queueMode,
+ Bundle params,
+ String utteranceId) {
+ if (!checkNonNull(caller, text, params)) {
+ return TextToSpeech.ERROR;
+ }
- SpeechItem item = new SynthesisSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text);
- return mSynthHandler.enqueueSpeechItem(queueMode, item);
- }
-
- @Override
- public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor
- fileDescriptor, Bundle params, String utteranceId) {
- if (!checkNonNull(caller, text, fileDescriptor, params)) {
- return TextToSpeech.ERROR;
- }
-
- // In test env, ParcelFileDescriptor instance may be EXACTLY the same
- // one that is used by client. And it will be closed by a client, thus
- // preventing us from writing anything to it.
- final ParcelFileDescriptor sameFileDescriptor = ParcelFileDescriptor.adoptFd(
- fileDescriptor.detachFd());
-
- SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text,
- new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor));
- return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
- }
-
- @Override
- public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params,
- String utteranceId) {
- if (!checkNonNull(caller, audioUri, params)) {
- return TextToSpeech.ERROR;
- }
-
- SpeechItem item = new AudioSpeechItemV1(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri);
- return mSynthHandler.enqueueSpeechItem(queueMode, item);
- }
-
- @Override
- public int playSilence(IBinder caller, long duration, int queueMode, String utteranceId) {
- if (!checkNonNull(caller)) {
- return TextToSpeech.ERROR;
- }
-
- SpeechItem item = new SilenceSpeechItem(caller,
- Binder.getCallingUid(), Binder.getCallingPid(), utteranceId, duration);
- return mSynthHandler.enqueueSpeechItem(queueMode, item);
- }
-
- @Override
- public boolean isSpeaking() {
- return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking();
- }
-
- @Override
- public int stop(IBinder caller) {
- if (!checkNonNull(caller)) {
- return TextToSpeech.ERROR;
- }
-
- return mSynthHandler.stopForApp(caller);
- }
-
- @Override
- public String[] getLanguage() {
- return onGetLanguage();
- }
-
- @Override
- public String[] getClientDefaultLanguage() {
- return getSettingsLocale();
- }
-
- /*
- * If defaults are enforced, then no language is "available" except
- * perhaps the default language selected by the user.
- */
- @Override
- public int isLanguageAvailable(String lang, String country, String variant) {
- if (!checkNonNull(lang)) {
- return TextToSpeech.ERROR;
- }
-
- return onIsLanguageAvailable(lang, country, variant);
- }
-
- @Override
- public String[] getFeaturesForLanguage(String lang, String country, String variant) {
- Set<String> features = onGetFeaturesForLanguage(lang, country, variant);
- String[] featuresArray = null;
- if (features != null) {
- featuresArray = new String[features.size()];
- features.toArray(featuresArray);
- } else {
- featuresArray = new String[0];
- }
- return featuresArray;
- }
-
- /*
- * There is no point loading a non default language if defaults
- * are enforced.
- */
- @Override
- public int loadLanguage(IBinder caller, String lang, String country, String variant) {
- if (!checkNonNull(lang)) {
- return TextToSpeech.ERROR;
- }
- int retVal = onIsLanguageAvailable(lang, country, variant);
-
- if (retVal == TextToSpeech.LANG_AVAILABLE ||
- retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
- retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
-
- SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(),
- Binder.getCallingPid(), lang, country, variant);
-
- if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) !=
- TextToSpeech.SUCCESS) {
- return TextToSpeech.ERROR;
+ SpeechItem item =
+ new SynthesisSpeechItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ params,
+ utteranceId,
+ text);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
}
- }
- return retVal;
- }
- @Override
- public List<Voice> getVoices() {
- return onGetVoices();
- }
+ @Override
+ public int synthesizeToFileDescriptor(
+ IBinder caller,
+ CharSequence text,
+ ParcelFileDescriptor fileDescriptor,
+ Bundle params,
+ String utteranceId) {
+ if (!checkNonNull(caller, text, fileDescriptor, params)) {
+ return TextToSpeech.ERROR;
+ }
- @Override
- public int loadVoice(IBinder caller, String voiceName) {
- if (!checkNonNull(voiceName)) {
- return TextToSpeech.ERROR;
- }
- int retVal = onIsValidVoiceName(voiceName);
+ // In test env, ParcelFileDescriptor instance may be EXACTLY the same
+ // one that is used by client. And it will be closed by a client, thus
+ // preventing us from writing anything to it.
+ final ParcelFileDescriptor sameFileDescriptor =
+ ParcelFileDescriptor.adoptFd(fileDescriptor.detachFd());
- if (retVal == TextToSpeech.SUCCESS) {
- SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(),
- Binder.getCallingPid(), voiceName);
- if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) !=
- TextToSpeech.SUCCESS) {
- return TextToSpeech.ERROR;
+ SpeechItem item =
+ new SynthesisToFileOutputStreamSpeechItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ params,
+ utteranceId,
+ text,
+ new ParcelFileDescriptor.AutoCloseOutputStream(
+ sameFileDescriptor));
+ return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
}
- }
- return retVal;
- }
- public String getDefaultVoiceNameFor(String lang, String country, String variant) {
- if (!checkNonNull(lang)) {
- return null;
- }
- int retVal = onIsLanguageAvailable(lang, country, variant);
+ @Override
+ public int playAudio(
+ IBinder caller,
+ Uri audioUri,
+ int queueMode,
+ Bundle params,
+ String utteranceId) {
+ if (!checkNonNull(caller, audioUri, params)) {
+ return TextToSpeech.ERROR;
+ }
- if (retVal == TextToSpeech.LANG_AVAILABLE ||
- retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE ||
- retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
- return onGetDefaultVoiceNameFor(lang, country, variant);
- } else {
- return null;
- }
- }
+ SpeechItem item =
+ new AudioSpeechItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ params,
+ utteranceId,
+ audioUri);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
- @Override
- public void setCallback(IBinder caller, ITextToSpeechCallback cb) {
- // Note that passing in a null callback is a valid use case.
- if (!checkNonNull(caller)) {
- return;
- }
+ @Override
+ public int playSilence(
+ IBinder caller, long duration, int queueMode, String utteranceId) {
+ if (!checkNonNull(caller)) {
+ return TextToSpeech.ERROR;
+ }
- mCallbacks.setCallback(caller, cb);
- }
+ SpeechItem item =
+ new SilenceSpeechItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ utteranceId,
+ duration);
+ return mSynthHandler.enqueueSpeechItem(queueMode, item);
+ }
- private String intern(String in) {
- // The input parameter will be non null.
- return in.intern();
- }
+ @Override
+ public boolean isSpeaking() {
+ return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking();
+ }
- private boolean checkNonNull(Object... args) {
- for (Object o : args) {
- if (o == null) return false;
- }
- return true;
- }
- };
+ @Override
+ public int stop(IBinder caller) {
+ if (!checkNonNull(caller)) {
+ return TextToSpeech.ERROR;
+ }
+
+ return mSynthHandler.stopForApp(caller);
+ }
+
+ @Override
+ public String[] getLanguage() {
+ return onGetLanguage();
+ }
+
+ @Override
+ public String[] getClientDefaultLanguage() {
+ return getSettingsLocale();
+ }
+
+ /*
+ * If defaults are enforced, then no language is "available" except
+ * perhaps the default language selected by the user.
+ */
+ @Override
+ public int isLanguageAvailable(String lang, String country, String variant) {
+ if (!checkNonNull(lang)) {
+ return TextToSpeech.ERROR;
+ }
+
+ return onIsLanguageAvailable(lang, country, variant);
+ }
+
+ @Override
+ public String[] getFeaturesForLanguage(
+ String lang, String country, String variant) {
+ Set<String> features = onGetFeaturesForLanguage(lang, country, variant);
+ String[] featuresArray = null;
+ if (features != null) {
+ featuresArray = new String[features.size()];
+ features.toArray(featuresArray);
+ } else {
+ featuresArray = new String[0];
+ }
+ return featuresArray;
+ }
+
+ /*
+ * There is no point loading a non default language if defaults
+ * are enforced.
+ */
+ @Override
+ public int loadLanguage(
+ IBinder caller, String lang, String country, String variant) {
+ if (!checkNonNull(lang)) {
+ return TextToSpeech.ERROR;
+ }
+ int retVal = onIsLanguageAvailable(lang, country, variant);
+
+ if (retVal == TextToSpeech.LANG_AVAILABLE
+ || retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE
+ || retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
+
+ SpeechItem item =
+ new LoadLanguageItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ lang,
+ country,
+ variant);
+
+ if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item)
+ != TextToSpeech.SUCCESS) {
+ return TextToSpeech.ERROR;
+ }
+ }
+ return retVal;
+ }
+
+ @Override
+ public List<Voice> getVoices() {
+ return onGetVoices();
+ }
+
+ @Override
+ public int loadVoice(IBinder caller, String voiceName) {
+ if (!checkNonNull(voiceName)) {
+ return TextToSpeech.ERROR;
+ }
+ int retVal = onIsValidVoiceName(voiceName);
+
+ if (retVal == TextToSpeech.SUCCESS) {
+ SpeechItem item =
+ new LoadVoiceItem(
+ caller,
+ Binder.getCallingUid(),
+ Binder.getCallingPid(),
+ voiceName);
+ if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item)
+ != TextToSpeech.SUCCESS) {
+ return TextToSpeech.ERROR;
+ }
+ }
+ return retVal;
+ }
+
+ public String getDefaultVoiceNameFor(String lang, String country, String variant) {
+ if (!checkNonNull(lang)) {
+ return null;
+ }
+ int retVal = onIsLanguageAvailable(lang, country, variant);
+
+ if (retVal == TextToSpeech.LANG_AVAILABLE
+ || retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE
+ || retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) {
+ return onGetDefaultVoiceNameFor(lang, country, variant);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void setCallback(IBinder caller, ITextToSpeechCallback cb) {
+ // Note that passing in a null callback is a valid use case.
+ if (!checkNonNull(caller)) {
+ return;
+ }
+
+ mCallbacks.setCallback(caller, cb);
+ }
+
+ private String intern(String in) {
+ // The input parameter will be non null.
+ return in.intern();
+ }
+
+ private boolean checkNonNull(Object... args) {
+ for (Object o : args) {
+ if (o == null) return false;
+ }
+ return true;
+ }
+ };
private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
private final HashMap<IBinder, ITextToSpeechCallback> mCallerToCallback
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 412eba3..a8c3453 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -471,9 +471,7 @@
String[] ret = new String[]{"","",""};
try {
// Note that the default locale might have an empty variant
- // or language, and we take care that the construction is
- // the same as {@link #getV1Locale} i.e no trailing delimiters
- // or spaces.
+ // or language.
ret[0] = locale.getISO3Language();
ret[1] = locale.getISO3Country();
ret[2] = locale.getVariant();
diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java
new file mode 100644
index 0000000..77d8596
--- /dev/null
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class provides more specific information about why the render process
+ * exited. The application may use this to decide how to handle the situation.
+ **/
+public abstract class RenderProcessGoneDetail {
+ /**
+ * Indicates whether the render process was observed to crash, or whether
+ * it was killed by the system.
+ *
+ * If the render process was killed, this is most likely caused by the
+ * system being low on memory.
+ *
+ * @return True if render process crashed, otherwise it was killed by
+ * system.
+ **/
+ public abstract boolean didCrash();
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 76d3fb1..8703468 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -466,4 +466,31 @@
public void onReceivedLoginRequest(WebView view, String realm,
String account, String args) {
}
+
+ /**
+ * Notify host application that the given webview's render process has exited.
+ *
+ * Multiple WebView instances may be associated with a single render process;
+ * onRenderProcessGone will be called for each WebView that was affected.
+ * The application's implementation of this callback should only attempt to
+ * clean up the specific WebView given as a parameter, and should not assume
+ * that other WebView instances are affected.
+ *
+ * The given WebView can't be used, and should be removed from the view hierarchy,
+ * all references to it should be cleaned up, e.g any references in the Activity
+ * or other classes saved using findViewById and similar calls, etc
+ *
+ * To cause an render process crash for test purpose, the application can
+ * call loadUrl("chrome://crash") on the WebView. Note that multiple WebView
+ * instances may be affected if they share a render process, not just the
+ * specific WebView which loaded chrome://crash.
+ *
+ * @param view The WebView which needs to be cleaned up.
+ * @param detail the reason why it exited.
+ * @return true if the host application handled the situation that process has
+ * exited, otherwise, application will crash.
+ */
+ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index c314cae..16a1251 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -69,6 +69,7 @@
import android.widget.BaseAdapter;
import android.widget.ListView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.TargetInfo;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -93,9 +94,13 @@
private IntentSender mRefinementIntentSender;
private RefinementResultReceiver mRefinementResultReceiver;
private ChooserTarget[] mCallerChooserTargets;
+ private ComponentName[] mFilteredComponentNames;
private Intent mReferrerFillInIntent;
+ private long mChooserShownTime;
+ private boolean mIsSuccessfullySelected;
+
private ChooserListAdapter mChooserListAdapter;
private ChooserRowAdapter mChooserRowAdapter;
@@ -155,6 +160,8 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ final long intentReceivedTime = System.currentTimeMillis();
+ mIsSuccessfullySelected = false;
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (!(targetParcelable instanceof Intent)) {
@@ -235,7 +242,7 @@
}
names[i] = (ComponentName) pa[i];
}
- setFilteredComponents(names);
+ mFilteredComponentNames = names;
}
pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
@@ -257,6 +264,13 @@
null, false);
MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
+
+ mChooserShownTime = System.currentTimeMillis();
+ final long systemCost = mChooserShownTime - intentReceivedTime;
+ MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost);
+ if (DEBUG) {
+ Log.d(TAG, "System Time Cost is " + systemCost);
+ }
}
static SharedPreferences getPinnedSharedPrefs(Context context) {
@@ -410,6 +424,7 @@
@Override
public void startSelected(int which, boolean always, boolean filtered) {
+ final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
super.startSelected(which, always, filtered);
if (mChooserListAdapter != null) {
@@ -435,6 +450,17 @@
if (cat != 0) {
MetricsLogger.action(this, cat, value);
}
+
+ if (mIsSuccessfullySelected) {
+ if (DEBUG) {
+ Log.d(TAG, "User Selection Time Cost is " + selectionCost);
+ Log.d(TAG, "position of selected app/service/caller is " +
+ Integer.toString(value));
+ }
+ MetricsLogger.histogram(null, "user_selection_cost_for_smart_sharing",
+ (int) selectionCost);
+ MetricsLogger.histogram(null, "app_position_for_smart_sharing", value);
+ }
}
}
@@ -563,6 +589,7 @@
if (ri != null && ri.activityInfo != null) {
usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
annotation, null, info.getResolvedIntent().getAction());
+ mResolverComparator.updateModel(info.getResolvedComponentName());
if (DEBUG) {
Log.d(TAG, "ResolveInfo Package is" + ri.activityInfo.packageName);
}
@@ -570,6 +597,7 @@
Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
}
}
+ mIsSuccessfullySelected = true;
}
void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
@@ -642,17 +670,65 @@
}
}
+ public class ChooserListController extends ResolverListController {
+ public ChooserListController(Context context,
+ PackageManager pm,
+ Intent targetIntent,
+ String referrerPackageName,
+ int launchedFromUid) {
+ super(context, pm, targetIntent, referrerPackageName, launchedFromUid);
+ }
+
+ @Override
+ boolean isComponentPinned(ComponentName name) {
+ return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
+ }
+
+ @Override
+ boolean isComponentFiltered(ComponentName name) {
+ if (mFilteredComponentNames == null) {
+ return false;
+ }
+ for (ComponentName filteredComponentName : mFilteredComponentNames) {
+ if (name.equals(filteredComponentName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public float getScore(DisplayResolveInfo target) {
+ if (target == null) {
+ return CALLER_TARGET_SCORE_BOOST;
+ }
+ float score = super.getScore(target);
+ if (target.isPinned()) {
+ score += PINNED_TARGET_SCORE_BOOST;
+ }
+ return score;
+ }
+ }
+
@Override
public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
boolean filterLastUsed) {
final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents,
- initialIntents, rList, launchedFromUid, filterLastUsed);
- if (DEBUG) Log.d(TAG, "Adapter created; querying services");
- queryTargetServices(adapter);
+ initialIntents, rList, launchedFromUid, filterLastUsed, createListController());
return adapter;
}
+ @VisibleForTesting
+ protected ResolverListController createListController() {
+ return new ChooserListController(
+ this,
+ mPm,
+ getTargetIntent(),
+ getReferrerPackageName(),
+ mLaunchedFromUid);
+ }
+
final class ChooserTargetInfo implements TargetInfo {
private final DisplayResolveInfo mSourceInfo;
private final ResolveInfo mBackupResolveInfo;
@@ -853,10 +929,11 @@
public ChooserListAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
- boolean filterLastUsed) {
+ boolean filterLastUsed, ResolverListController resolverListController) {
// Don't send the initial intents through the shared ResolverActivity path,
// we want to separate them into a different section.
- super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed);
+ super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed,
+ resolverListController);
if (initialIntents != null) {
final PackageManager pm = getPackageManager();
@@ -922,18 +999,6 @@
}
@Override
- public float getScore(DisplayResolveInfo target) {
- if (target == null) {
- return CALLER_TARGET_SCORE_BOOST;
- }
- float score = super.getScore(target);
- if (target.isPinned()) {
- score += PINNED_TARGET_SCORE_BOOST;
- }
- return score;
- }
-
- @Override
public View onCreateView(ViewGroup parent) {
return mInflater.inflate(
com.android.internal.R.layout.resolve_grid_item, parent, false);
@@ -944,6 +1009,8 @@
if (mServiceTargets != null) {
pruneServiceTargets();
}
+ if (DEBUG) Log.d(TAG, "List built querying services");
+ queryTargetServices(this);
}
@Override
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index b0e0373..5ce3e54 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -18,19 +18,23 @@
import com.android.internal.R;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.app.MediaRouteActionProvider;
import android.app.MediaRouteButton;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteGroup;
import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
+import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
-import android.view.Window;
-import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
@@ -46,7 +50,7 @@
*
* TODO: Move this back into the API, as in the support library media router.
*/
-public class MediaRouteControllerDialog extends Dialog {
+public class MediaRouteControllerDialog extends AlertDialog {
// Time to wait before updating the volume when the user lets go of the seek bar
// to allow the route provider time to propagate the change and publish a new
// route descriptor.
@@ -57,8 +61,9 @@
private final MediaRouter.RouteInfo mRoute;
private boolean mCreated;
- private Drawable mMediaRouteConnectingDrawable;
- private Drawable mMediaRouteOnDrawable;
+ private Drawable mMediaRouteButtonDrawable;
+ private int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
+ private int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
private Drawable mCurrentIconDrawable;
private boolean mVolumeControlEnabled = true;
@@ -68,8 +73,6 @@
private View mControlView;
- private Button mDisconnectButton;
-
public MediaRouteControllerDialog(Context context, int theme) {
super(context, theme);
@@ -132,14 +135,28 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ setTitle(mRoute.getName());
+ Resources res = getContext().getResources();
+ setButton(BUTTON_NEGATIVE, res.getString(R.string.media_route_controller_disconnect),
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int id) {
+ if (mRoute.isSelected()) {
+ mRouter.getDefaultRoute().select();
+ }
+ dismiss();
+ }
+ });
+ View customView = getLayoutInflater().inflate(R.layout.media_route_controller_dialog, null);
+ setView(customView, 0, 0, 0, 0);
super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
-
- setContentView(R.layout.media_route_controller_dialog);
-
- mVolumeLayout = (LinearLayout)findViewById(R.id.media_route_volume_layout);
- mVolumeSlider = (SeekBar)findViewById(R.id.media_route_volume_slider);
+ View customPanelView = getWindow().findViewById(R.id.customPanel);
+ if (customPanelView != null) {
+ customPanelView.setMinimumHeight(0);
+ }
+ mVolumeLayout = (LinearLayout) customView.findViewById(R.id.media_route_volume_layout);
+ mVolumeSlider = (SeekBar) customView.findViewById(R.id.media_route_volume_slider);
mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
private final Runnable mStopTrackingTouch = new Runnable() {
@Override
@@ -176,22 +193,12 @@
}
});
- mDisconnectButton = (Button)findViewById(R.id.media_route_disconnect_button);
- mDisconnectButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mRoute.isSelected()) {
- mRouter.getDefaultRoute().select();
- }
- dismiss();
- }
- });
-
+ mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
mCreated = true;
if (update()) {
mControlView = onCreateMediaControlView(savedInstanceState);
FrameLayout controlFrame =
- (FrameLayout)findViewById(R.id.media_route_control_frame);
+ (FrameLayout) customView.findViewById(R.id.media_route_control_frame);
if (mControlView != null) {
controlFrame.addView(mControlView);
controlFrame.setVisibility(View.VISIBLE);
@@ -201,7 +208,6 @@
}
}
-
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -248,24 +254,41 @@
Drawable icon = getIconDrawable();
if (icon != mCurrentIconDrawable) {
mCurrentIconDrawable = icon;
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, icon);
+ if (icon instanceof AnimationDrawable) {
+ AnimationDrawable animDrawable = (AnimationDrawable) icon;
+ if (!animDrawable.isRunning()) {
+ animDrawable.start();
+ }
+ }
+ setIcon(icon);
}
return true;
}
+ private Drawable obtainMediaRouteButtonDrawable() {
+ Context context = getContext();
+ TypedValue value = new TypedValue();
+ if (!context.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
+ return null;
+ }
+ int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
+ TypedArray a = context.obtainStyledAttributes(value.data, drawableAttrs);
+ Drawable drawable = a.getDrawable(0);
+ a.recycle();
+ return drawable;
+ }
+
private Drawable getIconDrawable() {
- if (mRoute.isConnecting()) {
- if (mMediaRouteConnectingDrawable == null) {
- mMediaRouteConnectingDrawable = getContext().getDrawable(
- R.drawable.ic_media_route_connecting_holo_dark);
- }
- return mMediaRouteConnectingDrawable;
+ if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
+ return mMediaRouteButtonDrawable;
+ } else if (mRoute.isConnecting()) {
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteConnectingState);
+ return stateListDrawable.getCurrent();
} else {
- if (mMediaRouteOnDrawable == null) {
- mMediaRouteOnDrawable = getContext().getDrawable(
- R.drawable.ic_media_route_on_holo_dark);
- }
- return mMediaRouteOnDrawable;
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteOnState);
+ return stateListDrawable.getCurrent();
}
}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
index 466c015..4c30501 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialogFragment.java
@@ -39,7 +39,6 @@
*/
public MediaRouteControllerDialogFragment() {
setCancelable(true);
- setStyle(STYLE_NORMAL, android.R.style.Theme_DeviceDefault_Dialog);
}
/**
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index c516b5c..f2bd701 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,15 +16,14 @@
package com.android.internal.app;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
+import android.annotation.UiThread;
import android.app.Activity;
import android.app.ActivityThread;
import android.app.VoiceInteractor.PickOptionRequest;
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.app.VoiceInteractor.Prompt;
-import android.content.pm.ComponentInfo;
import android.os.AsyncTask;
import android.os.RemoteException;
import android.provider.MediaStore;
@@ -33,6 +32,7 @@
import android.util.Slog;
import android.widget.AbsListView;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import android.app.ActivityManager;
@@ -75,7 +75,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -83,21 +82,16 @@
import java.util.Set;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
/**
* This activity is displayed when the system attempts to start an Intent for
* which there is more than one matching activity, allowing the user to decide
* which to go to. It is not normally used directly by application developers.
*/
+@UiThread
public class ResolverActivity extends Activity {
- private static final String TAG = "ResolverActivity";
- private static final boolean DEBUG = false;
- private int mLaunchedFromUid;
- private ResolveListAdapter mAdapter;
- private PackageManager mPm;
+ protected ResolveListAdapter mAdapter;
private boolean mSafeForwardingMode;
private boolean mAlwaysUseOption;
private AbsListView mAdapterView;
@@ -108,13 +102,19 @@
private int mLastSelected = AbsListView.INVALID_POSITION;
private boolean mResolvingHome = false;
private int mProfileSwitchMessageId = -1;
+ private int mLayoutId;
private final ArrayList<Intent> mIntents = new ArrayList<>();
- private ResolverComparator mResolverComparator;
private PickTargetOptionRequest mPickOptionRequest;
- private ComponentName[] mFilteredComponents;
+ private String mReferrerPackage;
+ protected ResolverComparator mResolverComparator;
protected ResolverDrawerLayout mResolverDrawerLayout;
protected String mContentType;
+ protected PackageManager mPm;
+ protected int mLaunchedFromUid;
+
+ private static final String TAG = "ResolverActivity";
+ private static final boolean DEBUG = false;
private boolean mRegistered;
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -261,6 +261,7 @@
mPackageMonitor.register(this, getMainLooper(), false);
mRegistered = true;
+ mReferrerPackage = getReferrerPackageName();
final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
mIconDpi = am.getLauncherLargeIconDensity();
@@ -268,11 +269,6 @@
// Add our initial intent as the first item, regardless of what else has already been added.
mIntents.add(0, new Intent(intent));
- final String referrerPackage = getReferrerPackageName();
-
- mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
- mContentType = mResolverComparator.mContentType;
-
if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
return;
}
@@ -306,11 +302,11 @@
if (titleIcon != null) {
ApplicationInfo ai = null;
try {
- if (!TextUtils.isEmpty(referrerPackage)) {
- ai = mPm.getApplicationInfo(referrerPackage, 0);
+ if (!TextUtils.isEmpty(mReferrerPackage)) {
+ ai = mPm.getApplicationInfo(mReferrerPackage, 0);
}
} catch (NameNotFoundException e) {
- Log.e(TAG, "Could not find referrer package " + referrerPackage);
+ Log.e(TAG, "Could not find referrer package " + mReferrerPackage);
}
if (ai != null) {
@@ -372,24 +368,6 @@
+ (categories != null ? Arrays.toString(categories.toArray()) : ""));
}
- public final void setFilteredComponents(ComponentName[] components) {
- mFilteredComponents = components;
- }
-
- public final boolean isComponentFiltered(ComponentInfo component) {
- if (mFilteredComponents == null) {
- return false;
- }
-
- final ComponentName checkName = component.getComponentName();
- for (ComponentName name : mFilteredComponents) {
- if (name.equals(checkName)) {
- return true;
- }
- }
- return false;
- }
-
/**
* Perform any initialization needed for voice interaction.
*/
@@ -431,7 +409,7 @@
return mIntents.isEmpty() ? null : mIntents.get(0);
}
- private String getReferrerPackageName() {
+ protected String getReferrerPackageName() {
final Uri referrer = getReferrer();
if (referrer != null && "android-app".equals(referrer.getScheme())) {
return referrer.getHost();
@@ -689,7 +667,7 @@
final Intent intent = target != null ? target.getResolvedIntent() : null;
if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem())
- && mAdapter.mOrigResolveList != null) {
+ && mAdapter.mUnfilteredResolveList != null) {
// Build a reasonable intent filter, based on what matched.
IntentFilter filter = new IntentFilter();
Intent filterIntent;
@@ -774,11 +752,11 @@
}
if (filter != null) {
- final int N = mAdapter.mOrigResolveList.size();
+ final int N = mAdapter.mUnfilteredResolveList.size();
ComponentName[] set = new ComponentName[N];
int bestMatch = 0;
for (int i=0; i<N; i++) {
- ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
+ ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
set[i] = new ComponentName(r.activityInfo.packageName,
r.activityInfo.name);
if (r.match > bestMatch) bestMatch = r.match;
@@ -899,7 +877,17 @@
Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
boolean filterLastUsed) {
return new ResolveListAdapter(context, payloadIntents, initialIntents, rList,
- launchedFromUid, filterLastUsed);
+ launchedFromUid, filterLastUsed, createListController());
+ }
+
+ @VisibleForTesting
+ protected ResolverListController createListController() {
+ return new ResolverListController(
+ this,
+ mPm,
+ getTargetIntent(),
+ getReferrerPackageName(),
+ mLaunchedFromUid);
}
/**
@@ -914,32 +902,38 @@
// to handle.
mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
mLaunchedFromUid, alwaysUseOption && !isVoiceInteraction());
+ boolean rebuildCompleted = mAdapter.rebuildList();
- final int layoutId;
if (mAdapter.hasFilteredItem()) {
- layoutId = R.layout.resolver_list_with_default;
+ mLayoutId = R.layout.resolver_list_with_default;
alwaysUseOption = false;
} else {
- layoutId = getLayoutResource();
+ mLayoutId = getLayoutResource();
}
mAlwaysUseOption = alwaysUseOption;
int count = mAdapter.getUnfilteredCount();
- if (count == 1 && mAdapter.getOtherProfile() == null) {
- // Only one target, so we're a candidate to auto-launch!
- final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
- if (shouldAutoLaunchSingleChoice(target)) {
- safelyStartActivity(target);
- mPackageMonitor.unregister();
- mRegistered = false;
- finish();
- return true;
+
+ // We only rebuild asynchronously when we have multiple elements to sort. In the case where
+ // we're already done, we can check if we should auto-launch immediately.
+ if (rebuildCompleted) {
+ if (count == 1 && mAdapter.getOtherProfile() == null) {
+ // Only one target, so we're a candidate to auto-launch!
+ final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+ if (shouldAutoLaunchSingleChoice(target)) {
+ safelyStartActivity(target);
+ mPackageMonitor.unregister();
+ mRegistered = false;
+ finish();
+ return true;
+ }
}
}
- if (count > 0) {
- setContentView(layoutId);
+
+ if (count > 0 || !rebuildCompleted) {
+ setContentView(mLayoutId);
mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
- onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
+ onPrepareAdapterView(mAdapterView, mAdapter, mAlwaysUseOption);
} else {
setContentView(R.layout.resolver_list);
@@ -1236,20 +1230,21 @@
private final List<ResolveInfo> mBaseResolveList;
private ResolveInfo mLastChosen;
private DisplayResolveInfo mOtherProfile;
- private final int mLaunchedFromUid;
private boolean mHasExtendedInfo;
+ private ResolverListController mResolverListController;
protected final LayoutInflater mInflater;
List<DisplayResolveInfo> mDisplayList;
- List<ResolvedComponentInfo> mOrigResolveList;
+ List<ResolvedComponentInfo> mUnfilteredResolveList;
private int mLastChosenPosition = -1;
private boolean mFilterLastUsed;
public ResolveListAdapter(Context context, List<Intent> payloadIntents,
Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
- boolean filterLastUsed) {
+ boolean filterLastUsed,
+ ResolverListController resolverListController) {
mIntents = payloadIntents;
mInitialIntents = initialIntents;
mBaseResolveList = rList;
@@ -1257,12 +1252,11 @@
mInflater = LayoutInflater.from(context);
mDisplayList = new ArrayList<>();
mFilterLastUsed = filterLastUsed;
- rebuildList();
+ mResolverListController = resolverListController;
}
public void handlePackagesChanged() {
rebuildList();
- notifyDataSetChanged();
if (getCount() == 0) {
// We no longer have any items... just finish the activity.
finish();
@@ -1293,12 +1287,17 @@
}
public float getScore(DisplayResolveInfo target) {
- return mResolverComparator.getScore(target.getResolvedComponentName());
+ return mResolverListController.getScore(target);
}
- private void rebuildList() {
+ /**
+ * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
+ * to complete.
+ *
+ * @return Whether or not the list building is completed.
+ */
+ protected boolean rebuildList() {
List<ResolvedComponentInfo> currentResolveList = null;
-
try {
final Intent primaryIntent = getTargetIntent();
mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
@@ -1312,84 +1311,88 @@
mOtherProfile = null;
mDisplayList.clear();
if (mBaseResolveList != null) {
- currentResolveList = mOrigResolveList = new ArrayList<>();
- addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
+ currentResolveList = mUnfilteredResolveList = new ArrayList<>();
+ mResolverListController.addResolveListDedupe(currentResolveList,
+ getTargetIntent(),
+ mBaseResolveList);
} else {
- final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
- final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
- for (int i = 0, N = mIntents.size(); i < N; i++) {
- final Intent intent = mIntents.get(i);
- final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY
- | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
- | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
- if (infos != null) {
- if (currentResolveList == null) {
- currentResolveList = mOrigResolveList = new ArrayList<>();
- }
- addResolveListDedupe(currentResolveList, intent, infos);
- }
+ currentResolveList =
+ mResolverListController.getResolversForIntent(shouldGetResolvedFilter(),
+ shouldGetActivityMetadata(),
+ mIntents);
+ if (currentResolveList == null) {
+ processSortedList(currentResolveList);
+ return true;
}
-
- // Filter out any activities that the launched uid does not
- // have permission for.
- // Also filter out those that are suspended because they couldn't
- // be started. We don't do this when we have an explicit
- // list of resolved activities, because that only happens when
- // we are being subclassed, so we can safely launch whatever
- // they gave us.
- if (currentResolveList != null) {
- for (int i=currentResolveList.size()-1; i >= 0; i--) {
- ActivityInfo ai = currentResolveList.get(i)
- .getResolveInfoAt(0).activityInfo;
- int granted = ActivityManager.checkComponentPermission(
- ai.permission, mLaunchedFromUid,
- ai.applicationInfo.uid, ai.exported);
- boolean suspended = (ai.applicationInfo.flags
- & ApplicationInfo.FLAG_SUSPENDED) != 0;
- if (granted != PackageManager.PERMISSION_GRANTED || suspended
- || isComponentFiltered(ai)) {
- // Access not allowed!
- if (mOrigResolveList == currentResolveList) {
- mOrigResolveList = new ArrayList<>(mOrigResolveList);
- }
- currentResolveList.remove(i);
- }
- }
+ List<ResolvedComponentInfo> originalList =
+ mResolverListController.filterIneligibleActivities(currentResolveList,
+ true);
+ if (originalList != null) {
+ mUnfilteredResolveList = originalList;
}
}
int N;
if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
- // Only display the first matches that are either of equal
- // priority or have asked to be default options.
- ResolvedComponentInfo rci0 = currentResolveList.get(0);
- ResolveInfo r0 = rci0.getResolveInfoAt(0);
- for (int i=1; i<N; i++) {
- ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
- if (DEBUG) Log.v(
- TAG,
- r0.activityInfo.name + "=" +
- r0.priority + "/" + r0.isDefault + " vs " +
- ri.activityInfo.name + "=" +
- ri.priority + "/" + ri.isDefault);
- if (r0.priority != ri.priority ||
- r0.isDefault != ri.isDefault) {
- while (i < N) {
- if (mOrigResolveList == currentResolveList) {
- mOrigResolveList = new ArrayList<>(mOrigResolveList);
- }
- currentResolveList.remove(i);
- N--;
- }
- }
+ // We only care about fixing the unfilteredList if the current resolve list and
+ // current resolve list are currently the same.
+ List<ResolvedComponentInfo> originalList =
+ mResolverListController.filterLowPriority(currentResolveList,
+ mUnfilteredResolveList == currentResolveList);
+ if (originalList != null) {
+ mUnfilteredResolveList = originalList;
}
+
if (N > 1) {
- mResolverComparator.compute(currentResolveList);
- Collections.sort(currentResolveList, mResolverComparator);
+ AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>> sortingTask =
+ new AsyncTask<List<ResolvedComponentInfo>,
+ Void,
+ List<ResolvedComponentInfo>>() {
+ @Override
+ protected List<ResolvedComponentInfo> doInBackground(
+ List<ResolvedComponentInfo>... params) {
+ mResolverListController.sort(params[0]);
+ return params[0];
+ }
+
+ @Override
+ protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
+ processSortedList(sortedComponents);
+ onPrepareAdapterView(mAdapterView, mAdapter, mAlwaysUseOption);
+ if (mProfileView != null) {
+ bindProfileView();
+ }
+ }
+ };
+ sortingTask.execute(currentResolveList);
+ return false;
+ } else {
+ processSortedList(currentResolveList);
+ return true;
}
+ } else {
+ processSortedList(currentResolveList);
+ return true;
+ }
+ }
+
+ private void disableLastChosenIfNeeded() {
+ // Layout doesn't handle both profile button and last chosen
+ // so disable last chosen if profile button is present.
+ if (mOtherProfile != null && mLastChosenPosition >= 0) {
+ mLastChosenPosition = -1;
+ mFilterLastUsed = false;
+ }
+ }
+
+
+ private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
+ int N;
+ if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
// First put the initial items at the top.
if (mInitialIntents != null) {
- for (int i=0; i<mInitialIntents.length; i++) {
+ for (int i = 0; i < mInitialIntents.length; i++) {
Intent ii = mInitialIntents[i];
if (ii == null) {
continue;
@@ -1405,7 +1408,7 @@
UserManager userManager =
(UserManager) getSystemService(Context.USER_SERVICE);
if (ii instanceof LabeledIntent) {
- LabeledIntent li = (LabeledIntent)ii;
+ LabeledIntent li = (LabeledIntent) ii;
ri.resolvePackageName = li.getSourcePackage();
ri.labelRes = li.getLabelResource();
ri.nonLocalizedLabel = li.getNonLocalizedLabel();
@@ -1423,16 +1426,16 @@
// Check for applications with same name and use application name or
// package name if necessary
- rci0 = currentResolveList.get(0);
- r0 = rci0.getResolveInfoAt(0);
+ ResolvedComponentInfo rci0 = sortedComponents.get(0);
+ ResolveInfo r0 = rci0.getResolveInfoAt(0);
int start = 0;
- CharSequence r0Label = r0.loadLabel(mPm);
+ CharSequence r0Label = r0.loadLabel(mPm);
mHasExtendedInfo = false;
for (int i = 1; i < N; i++) {
if (r0Label == null) {
r0Label = r0.activityInfo.packageName;
}
- ResolvedComponentInfo rci = currentResolveList.get(i);
+ ResolvedComponentInfo rci = sortedComponents.get(i);
ResolveInfo ri = rci.getResolveInfoAt(0);
CharSequence riLabel = ri.loadLabel(mPm);
if (riLabel == null) {
@@ -1441,59 +1444,19 @@
if (riLabel.equals(r0Label)) {
continue;
}
- processGroup(currentResolveList, start, (i-1), rci0, r0Label);
+ processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
rci0 = rci;
r0 = ri;
r0Label = riLabel;
start = i;
}
// Process last group
- processGroup(currentResolveList, start, (N-1), rci0, r0Label);
+ processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
}
-
- // Layout doesn't handle both profile button and last chosen
- // so disable last chosen if profile button is present.
- if (mOtherProfile != null && mLastChosenPosition >= 0) {
- mLastChosenPosition = -1;
- mFilterLastUsed = false;
- }
-
+ disableLastChosenIfNeeded();
onListRebuilt();
}
- private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
- List<ResolveInfo> from) {
- final int fromCount = from.size();
- final int intoCount = into.size();
- for (int i = 0; i < fromCount; i++) {
- final ResolveInfo newInfo = from.get(i);
- boolean found = false;
- // Only loop to the end of into as it was before we started; no dupes in from.
- for (int j = 0; j < intoCount; j++) {
- final ResolvedComponentInfo rci = into.get(j);
- if (isSameResolvedComponent(newInfo, rci)) {
- found = true;
- rci.add(intent, newInfo);
- break;
- }
- }
- if (!found) {
- final ComponentName name = new ComponentName(
- newInfo.activityInfo.packageName, newInfo.activityInfo.name);
- final ResolvedComponentInfo rci = new ResolvedComponentInfo(name,
- intent, newInfo);
- rci.setPinned(isComponentPinned(name));
- into.add(rci);
- }
- }
- }
-
- private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
- final ActivityInfo ai = a.activityInfo;
- return ai.packageName.equals(b.name.getPackageName())
- && ai.name.equals(b.name.getClassName());
- }
-
public void onListRebuilt() {
// This space for rent
}
@@ -1715,7 +1678,8 @@
}
}
- static final class ResolvedComponentInfo {
+ @VisibleForTesting
+ public static final class ResolvedComponentInfo {
public final ComponentName name;
private boolean mPinned;
private final List<Intent> mIntents = new ArrayList<>();
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 75be906..45fad97 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -27,11 +27,16 @@
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.os.storage.StorageManager;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Comparator;
@@ -54,6 +59,12 @@
private static final float RECENCY_MULTIPLIER = 2.f;
+ // feature names used in ranking.
+ private static final String LAUNCH_SCORE = "launch";
+ private static final String TIME_SPENT_SCORE = "timeSpent";
+ private static final String RECENCY_SCORE = "recency";
+ private static final String CHOOSER_SCORE = "chooser";
+
private final Collator mCollator;
private final boolean mHttp;
private final PackageManager mPm;
@@ -65,6 +76,7 @@
private final String mReferrerPackage;
public String mContentType;
private String mAction;
+ private LogisticRegressionAppRanker mRanker;
public ResolverComparator(Context context, Intent intent, String referrerPackage) {
mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
@@ -80,6 +92,7 @@
mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
mContentType = intent.getType();
mAction = intent.getAction();
+ mRanker = new LogisticRegressionAppRanker(context);
}
public void compute(List<ResolvedComponentInfo> targets) {
@@ -152,16 +165,13 @@
for (ScoredTarget target : mScoredTargets.values()) {
final float recency = (float) Math.max(target.lastTimeUsed - recentSinceTime, 0)
/ (mostRecentlyUsedTime - recentSinceTime);
- final float recencyScore = recency * recency * RECENCY_MULTIPLIER;
- final float usageTimeScore = (float) target.timeSpent / mostTimeSpent;
- final float launchCountScore = (float) target.launchCount / mostLaunched;
-
- target.score = recencyScore + usageTimeScore + launchCountScore;
+ target.setFeatures((float) target.launchCount / mostLaunched,
+ (float) target.timeSpent / mostTimeSpent,
+ recency * recency * RECENCY_MULTIPLIER,
+ (float) target.chooserCount / mostSelected);
+ target.selectProb = mRanker.predict(target.getFeatures());
if (DEBUG) {
- Log.d(TAG, "Scores: recencyScore: " + recencyScore
- + " usageTimeScore: " + usageTimeScore
- + " launchCountScore: " + launchCountScore
- + " - " + target);
+ Log.d(TAG, "Scores: " + target);
}
}
}
@@ -215,17 +225,11 @@
final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
rhs.activityInfo.packageName, rhs.activityInfo.name));
- final int chooserCountDiff = Long.compare(
- rhsTarget.chooserCount, lhsTarget.chooserCount);
+ final int selectProbDiff = Float.compare(
+ rhsTarget.selectProb, lhsTarget.selectProb);
- if (chooserCountDiff != 0) {
- return chooserCountDiff > 0 ? 1 : -1;
- }
-
- final int diff = Float.compare(rhsTarget.score, lhsTarget.score);
-
- if (diff != 0) {
- return diff > 0 ? 1 : -1;
+ if (selectProbDiff != 0) {
+ return selectProbDiff > 0 ? 1 : -1;
}
}
}
@@ -241,32 +245,160 @@
public float getScore(ComponentName name) {
final ScoredTarget target = mScoredTargets.get(name);
if (target != null) {
- return target.score;
+ return target.selectProb;
}
return 0;
}
static class ScoredTarget {
public final ComponentInfo componentInfo;
- public float score;
public long lastTimeUsed;
public long timeSpent;
public long launchCount;
public long chooserCount;
+ public ArrayMap<String, Float> features;
+ public float selectProb;
public ScoredTarget(ComponentInfo ci) {
componentInfo = ci;
+ features = new ArrayMap<>(5);
}
@Override
public String toString() {
return "ScoredTarget{" + componentInfo
- + " score: " + score
+ " lastTimeUsed: " + lastTimeUsed
+ " timeSpent: " + timeSpent
+ " launchCount: " + launchCount
+ " chooserCount: " + chooserCount
+ + " selectProb: " + selectProb
+ "}";
}
+
+ public void setFeatures(float launchCountScore, float usageTimeScore, float recencyScore,
+ float chooserCountScore) {
+ features.put(LAUNCH_SCORE, launchCountScore);
+ features.put(TIME_SPENT_SCORE, usageTimeScore);
+ features.put(RECENCY_SCORE, recencyScore);
+ features.put(CHOOSER_SCORE, chooserCountScore);
+ }
+
+ public ArrayMap<String, Float> getFeatures() {
+ return features;
+ }
+ }
+
+ public void updateModel(ComponentName componentName) {
+ if (mScoredTargets == null || componentName == null ||
+ !mScoredTargets.containsKey(componentName)) {
+ return;
+ }
+ ScoredTarget selected = mScoredTargets.get(componentName);
+ for (ComponentName targetComponent : mScoredTargets.keySet()) {
+ if (targetComponent.equals(componentName)) {
+ continue;
+ }
+ ScoredTarget target = mScoredTargets.get(targetComponent);
+ // A potential point of optimization. Save updates or derive a closed form for the
+ // positive case, to avoid calculating them repeatedly.
+ if (target.selectProb >= selected.selectProb) {
+ mRanker.update(target.getFeatures(), target.selectProb, false);
+ mRanker.update(selected.getFeatures(), selected.selectProb, true);
+ }
+ }
+ mRanker.commitUpdate();
+ }
+
+ class LogisticRegressionAppRanker {
+ private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
+ private static final String BIAS_PREF_KEY = "bias";
+ private static final float LEARNING_RATE = 0.02f;
+ private static final float REGULARIZER_PARAM = 0.1f;
+ private SharedPreferences mParamSharedPref;
+ private ArrayMap<String, Float> mFeatureWeights;
+ private float mBias;
+
+ public LogisticRegressionAppRanker(Context context) {
+ mParamSharedPref = getParamSharedPref(context);
+ }
+
+ public float predict(ArrayMap<String, Float> target) {
+ if (target == null || mParamSharedPref == null) {
+ return 0.0f;
+ }
+ final int featureSize = target.size();
+ if (featureSize == 0) {
+ return 0.0f;
+ }
+ float sum = 0.0f;
+ if (mFeatureWeights == null) {
+ mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
+ mFeatureWeights = new ArrayMap<>(featureSize);
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float weight = mParamSharedPref.getFloat(featureName, 0.0f);
+ sum += weight * target.valueAt(i);
+ mFeatureWeights.put(featureName, weight);
+ }
+ } else {
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ sum += weight * target.valueAt(i);
+ }
+ }
+ return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
+ }
+
+ public void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
+ if (target == null || target.size() == 0) {
+ return;
+ }
+ final int featureSize = target.size();
+ if (mFeatureWeights == null) {
+ mBias = 0.0f;
+ mFeatureWeights = new ArrayMap<>(featureSize);
+ }
+ float error = isSelected ? 1.0f - predict : -predict;
+ for (int i = 0; i < featureSize; i++) {
+ String featureName = target.keyAt(i);
+ float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
+ mBias += LEARNING_RATE * error;
+ currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
+ LEARNING_RATE * error * target.valueAt(i);
+ mFeatureWeights.put(featureName, currentWeight);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
+ }
+ }
+
+ public void commitUpdate() {
+ if (mFeatureWeights == null || mFeatureWeights.size() == 0) {
+ return;
+ }
+ SharedPreferences.Editor editor = mParamSharedPref.edit();
+ editor.putFloat(BIAS_PREF_KEY, mBias);
+ final int size = mFeatureWeights.size();
+ for (int i = 0; i < size; i++) {
+ editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
+ }
+ editor.apply();
+ }
+
+ private SharedPreferences getParamSharedPref(Context context) {
+ // The package info in the context isn't initialized in the way it is for normal apps,
+ // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
+ // build the path manually below using the same policy that appears in ContextImpl.
+ if (DEBUG) {
+ Log.d(TAG, "Context Package Name: " + context.getPackageName());
+ }
+ final File prefsFile = new File(new File(
+ Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
+ context.getUserId(), context.getPackageName()),
+ "shared_prefs"),
+ PARAM_SHARED_PREF_NAME + ".xml");
+ return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ }
}
}
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
new file mode 100644
index 0000000..b91ecb6
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.internal.app;
+
+import android.annotation.WorkerThread;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A helper for the ResolverActivity that exposes methods to retrieve, filter and sort its list of
+ * resolvers.
+ */
+public class ResolverListController {
+
+ private final Context mContext;
+ private final PackageManager mpm;
+ private final int mLaunchedFromUid;
+
+ // Needed for sorting resolvers.
+ private final Intent mTargetIntent;
+ private final String mReferrerPackage;
+
+ private static final String TAG = "ResolverListController";
+ private static final boolean DEBUG = false;
+
+ private ResolverComparator mResolverComparator;
+
+ public ResolverListController(
+ Context context,
+ PackageManager pm,
+ Intent targetIntent,
+ String referrerPackage,
+ int launchedFromUid) {
+ mContext = context;
+ mpm = pm;
+ mLaunchedFromUid = launchedFromUid;
+ mTargetIntent = targetIntent;
+ mReferrerPackage = referrerPackage;
+ }
+
+ @VisibleForTesting
+ public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
+ boolean shouldGetResolvedFilter,
+ boolean shouldGetActivityMetadata,
+ List<Intent> intents) {
+ List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null;
+ for (int i = 0, N = intents.size(); i < N; i++) {
+ final Intent intent = intents.get(i);
+ final List<ResolveInfo> infos = mpm.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
+ | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
+ if (infos != null) {
+ if (resolvedComponents == null) {
+ resolvedComponents = new ArrayList<>();
+ }
+ addResolveListDedupe(resolvedComponents, intent, infos);
+ }
+ }
+ return resolvedComponents;
+ }
+
+ @VisibleForTesting
+ public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into,
+ Intent intent,
+ List<ResolveInfo> from) {
+ final int fromCount = from.size();
+ final int intoCount = into.size();
+ for (int i = 0; i < fromCount; i++) {
+ final ResolveInfo newInfo = from.get(i);
+ boolean found = false;
+ // Only loop to the end of into as it was before we started; no dupes in from.
+ for (int j = 0; j < intoCount; j++) {
+ final ResolverActivity.ResolvedComponentInfo rci = into.get(j);
+ if (isSameResolvedComponent(newInfo, rci)) {
+ found = true;
+ rci.add(intent, newInfo);
+ break;
+ }
+ }
+ if (!found) {
+ final ComponentName name = new ComponentName(
+ newInfo.activityInfo.packageName, newInfo.activityInfo.name);
+ final ResolverActivity.ResolvedComponentInfo rci =
+ new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo);
+ rci.setPinned(isComponentPinned(name));
+ into.add(rci);
+ }
+ }
+ }
+
+ // Filter out any activities that the launched uid does not have permission for.
+ //
+ // Also filter out those that are suspended because they couldn't be started. We don't do this
+ // when we have an explicit list of resolved activities, because that only happens when
+ // we are being subclassed, so we can safely launch whatever they gave us.
+ //
+ // To preserve the inputList, optionally will return the original list if any modification has
+ // been made.
+ @VisibleForTesting
+ public ArrayList<ResolverActivity.ResolvedComponentInfo> filterIneligibleActivities(
+ List<ResolverActivity.ResolvedComponentInfo> inputList,
+ boolean returnCopyOfOriginalListIfModified) {
+ ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null;
+ for (int i = inputList.size()-1; i >= 0; i--) {
+ ActivityInfo ai = inputList.get(i)
+ .getResolveInfoAt(0).activityInfo;
+ int granted = ActivityManager.checkComponentPermission(
+ ai.permission, mLaunchedFromUid,
+ ai.applicationInfo.uid, ai.exported);
+ boolean suspended = (ai.applicationInfo.flags
+ & ApplicationInfo.FLAG_SUSPENDED) != 0;
+ if (granted != PackageManager.PERMISSION_GRANTED || suspended
+ || isComponentFiltered(ai.getComponentName())) {
+ // Access not allowed! We're about to filter an item,
+ // so modify the unfiltered version if it hasn't already been modified.
+ if (returnCopyOfOriginalListIfModified && listToReturn == null) {
+ listToReturn = new ArrayList<>(inputList);
+ }
+ inputList.remove(i);
+ }
+ }
+ return listToReturn;
+ }
+
+ // Filter out any low priority items.
+ //
+ // To preserve the inputList, optionally will return the original list if any modification has
+ // been made.
+ @VisibleForTesting
+ public ArrayList<ResolverActivity.ResolvedComponentInfo> filterLowPriority(
+ List<ResolverActivity.ResolvedComponentInfo> inputList,
+ boolean returnCopyOfOriginalListIfModified) {
+ ArrayList<ResolverActivity.ResolvedComponentInfo> listToReturn = null;
+ // Only display the first matches that are either of equal
+ // priority or have asked to be default options.
+ ResolverActivity.ResolvedComponentInfo rci0 = inputList.get(0);
+ ResolveInfo r0 = rci0.getResolveInfoAt(0);
+ int N = inputList.size();
+ for (int i = 1; i < N; i++) {
+ ResolveInfo ri = inputList.get(i).getResolveInfoAt(0);
+ if (DEBUG) Log.v(
+ TAG,
+ r0.activityInfo.name + "=" +
+ r0.priority + "/" + r0.isDefault + " vs " +
+ ri.activityInfo.name + "=" +
+ ri.priority + "/" + ri.isDefault);
+ if (r0.priority != ri.priority ||
+ r0.isDefault != ri.isDefault) {
+ while (i < N) {
+ if (returnCopyOfOriginalListIfModified && listToReturn == null) {
+ listToReturn = new ArrayList<>(inputList);
+ }
+ inputList.remove(i);
+ N--;
+ }
+ }
+ }
+ return listToReturn;
+ }
+
+ @VisibleForTesting
+ @WorkerThread
+ public void sort(List<ResolverActivity.ResolvedComponentInfo> inputList) {
+ if (mResolverComparator == null) {
+ mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+ }
+ mResolverComparator.compute(inputList);
+ Collections.sort(inputList, mResolverComparator);
+ }
+
+ private static boolean isSameResolvedComponent(ResolveInfo a,
+ ResolverActivity.ResolvedComponentInfo b) {
+ final ActivityInfo ai = a.activityInfo;
+ return ai.packageName.equals(b.name.getPackageName())
+ && ai.name.equals(b.name.getClassName());
+ }
+
+ boolean isComponentPinned(ComponentName name) {
+ return false;
+ }
+
+ boolean isComponentFiltered(ComponentName componentName) {
+ return false;
+ }
+
+ @VisibleForTesting
+ public float getScore(ResolverActivity.DisplayResolveInfo target) {
+ if (mResolverComparator == null) {
+ mResolverComparator = new ResolverComparator(mContext, mTargetIntent, mReferrerPackage);
+ }
+ return mResolverComparator.getScore(target.getResolvedComponentName());
+ }
+}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8f74bf8..59cbc93 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -52,6 +52,7 @@
mAllocationSize = mBitmap->getAllocationByteCount();
mRowBytes = mBitmap->rowBytes();
mGenerationId = mBitmap->getGenerationID();
+ mIsHardware = mBitmap->isHardware();
mBitmap.reset();
}
@@ -118,6 +119,13 @@
return mGenerationId;
}
+ bool isHardware() {
+ if (mBitmap) {
+ return mBitmap->isHardware();
+ }
+ return mIsHardware;
+ }
+
~BitmapWrapper() { }
private:
@@ -127,6 +135,7 @@
size_t mAllocationSize;
size_t mRowBytes;
uint32_t mGenerationId;
+ bool mIsHardware;
};
// Convenience class that does not take a global ref on the pixels, relying
@@ -775,7 +784,7 @@
static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- if (bitmap->bitmap().isHardware()) {
+ if (bitmap->isHardware()) {
return GraphicsJNI::hardwareLegacyBitmapConfig();
}
return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
@@ -1208,7 +1217,7 @@
// Paying the price for making Hardware Bitmap as Config:
// later check for colorType will pass successfully,
// because Hardware Config internally may be RGBA8888 or smth like that.
- if (bitmap0->bitmap().isHardware() != bitmap1->bitmap().isHardware()) {
+ if (bitmap0->isHardware() != bitmap1->isHardware()) {
return JNI_FALSE;
}
@@ -1282,6 +1291,23 @@
return static_cast<jint>(bitmapHandle->getAllocationByteCount());
}
+static jobject Bitmap_nativeCopyPreserveInternalConfig(JNIEnv* env, jobject, jlong bitmapPtr) {
+ LocalScopedBitmap bitmapHandle(bitmapPtr);
+ LOG_ALWAYS_FATAL_IF(!bitmapHandle->isHardware(),
+ "Hardware config is only supported config in Bitmap_nativeCopyPreserveInternalConfig");
+ Bitmap& hwuiBitmap = bitmapHandle->bitmap();
+ SkBitmap src;
+ hwuiBitmap.getSkBitmap(&src);
+
+ SkBitmap result;
+ HeapAllocator allocator;
+ if (!src.copyTo(&result, hwuiBitmap.info().colorType(), &allocator)) {
+ doThrowRE(env, "Could not copy a hardware bitmap.");
+ return NULL;
+ }
+ return createBitmap(env, allocator.getStorageObjAndReset(), kBitmapCreateFlag_None);
+}
+
///////////////////////////////////////////////////////////////////////////////
static jclass make_globalref(JNIEnv* env, const char classname[])
{
@@ -1340,6 +1366,8 @@
{ "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
{ "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
{ "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
+ { "nativeCopyPreserveInternalConfig", "(J)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_nativeCopyPreserveInternalConfig },
};
int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index 035417e..92c7746 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -121,7 +121,7 @@
int transparent, int width)
{
for (; width > 0; width--, src++, dst++) {
- if (*src != transparent) {
+ if (*src != transparent && *src < cmap->ColorCount) {
const GifColorType& col = cmap->Colors[*src];
*dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 664c7ea..226e9e3 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -463,6 +463,18 @@
}
static jint
+android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, jint device, jstring device_address, jstring device_name)
+{
+ const char *c_address = env->GetStringUTFChars(device_address, NULL);
+ const char *c_name = env->GetStringUTFChars(device_name, NULL);
+ int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device),
+ c_address, c_name));
+ env->ReleaseStringUTFChars(device_address, c_address);
+ env->ReleaseStringUTFChars(device_name, c_name);
+ return (jint) status;
+}
+
+static jint
android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
{
return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
@@ -1764,6 +1776,7 @@
{"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
{"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
{"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState},
+ {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
{"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
{"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
{"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 95e031b..c456d62 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -52,6 +52,8 @@
jmethodID get;
} gArrayListMethods;
+static jclass gErrorClass;
+
static struct fields_t {
jfieldID contextID;
jmethodID onTransactID;
@@ -144,6 +146,22 @@
replyObj.get(),
flags);
+ if (env->ExceptionCheck()) {
+ jthrowable excep = env->ExceptionOccurred();
+ env->ExceptionDescribe();
+
+ if (env->IsInstanceOf(excep, gErrorClass)) {
+ /* It's an error */
+ LOG(ERROR) << "Forcefully exiting";
+ exit(1);
+ } else {
+ env->ExceptionClear();
+ LOG(ERROR) << "Uncaught exception!";
+ }
+
+ env->DeleteLocalRef(excep);
+ }
+
status_t err = OK;
if (!replyContext->wasSent()) {
@@ -356,6 +374,9 @@
gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
+ jclass errorClass = FindClassOrDie(env, "java/lang/Error");
+ gErrorClass = MakeGlobalRefOrDie(env, errorClass);
+
return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a10d807..b9d810a 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -49,7 +49,7 @@
} gFields;
-void signalExceptionForError(JNIEnv *env, status_t err) {
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException) {
switch (err) {
case OK:
break;
@@ -114,8 +114,13 @@
default:
{
+ std::stringstream ss;
+ ss << "HwBinder Error: (" << err << ")";
+
jniThrowException(
- env, "java/lang/RuntimeException", "Unknown error");
+ env,
+ canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
+ ss.str().c_str());
break;
}
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index 708bbba..f81de9b 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -67,7 +67,7 @@
DISALLOW_COPY_AND_ASSIGN(JHwParcel);
};
-void signalExceptionForError(JNIEnv *env, status_t err);
+void signalExceptionForError(JNIEnv *env, status_t err, bool canThrowRemoteException = false);
int register_android_os_HwParcel(JNIEnv *env);
} // namespace android
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 0a7d84d..f2f8e52 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -347,7 +347,7 @@
JHwParcel::GetNativeContext(env, replyObj)->getParcel();
status_t err = binder->transact(code, *request, reply, flags);
- signalExceptionForError(env, err);
+ signalExceptionForError(env, err, true /* canThrowRemoteException */);
}
static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68d8c55..4c9fb04 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -168,6 +168,8 @@
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.a2dp.profile.action.CODEC_CONFIG_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
@@ -2075,6 +2077,7 @@
@SystemApi @hide -->
<permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"
android:protectionLevel="signature" />
+ <uses-permission android:name="android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"/>
<!-- ========================================= -->
<!-- Permissions for special development tools -->
diff --git a/core/res/res/layout/media_route_controller_dialog.xml b/core/res/res/layout/media_route_controller_dialog.xml
index 0bf70da..24a2535 100644
--- a/core/res/res/layout/media_route_controller_dialog.xml
+++ b/core/res/res/layout/media_route_controller_dialog.xml
@@ -47,17 +47,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
-
- <!-- Disconnect button. -->
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="?attr/buttonBarStyle">
- <Button android:id="@+id/media_route_disconnect_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- style="?attr/buttonBarButtonStyle"
- android:gravity="center"
- android:text="@string/media_route_controller_disconnect" />
- </LinearLayout>
</LinearLayout>
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 989a79d..d02e156 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1482,10 +1482,10 @@
<java-symbol type="id" name="media_route_volume_layout" />
<java-symbol type="id" name="media_route_volume_slider" />
<java-symbol type="id" name="media_route_control_frame" />
- <java-symbol type="id" name="media_route_disconnect_button" />
<java-symbol type="id" name="media_route_extended_settings_button" />
<java-symbol type="string" name="media_route_chooser_title" />
<java-symbol type="string" name="media_route_chooser_title_for_remote_display" />
+ <java-symbol type="string" name="media_route_controller_disconnect" />
<java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
<java-symbol type="dimen" name="config_minScalingSpan" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ba1a55d..cd41987 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1156,6 +1156,7 @@
</activity>
<activity android:name="android.app.EmptyActivity">
</activity>
+ <activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
<receiver android:name="android.app.activity.AbortReceiver">
<intent-filter android:priority="1">
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index 9c3346e..e818c56 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -166,4 +166,52 @@
assertTrue(newNetwork.meteredHint);
assertNull(newNetwork.attributes);
}
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadgeWhenNoAttributesBundle() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadgeWhenNoBadgingCurveInBundle() {
+ ScoredNetwork network = new ScoredNetwork(KEY, CURVE, false /* meteredHint */, ATTRIBUTES);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturn4kBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_4K);
+ assertEquals(ScoredNetwork.BADGING_4K, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnHdBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_HD);
+ assertEquals(ScoredNetwork.BADGING_HD, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnSdBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_SD);
+ assertEquals(ScoredNetwork.BADGING_SD, network.calculateBadge(TEST_RSSI));
+ }
+
+ @Test
+ public void calculateBadgeShouldReturnNoBadge() {
+ ScoredNetwork network =
+ buildScoredNetworkWithGivenBadgeForTestRssi(ScoredNetwork.BADGING_NONE);
+ assertEquals(ScoredNetwork.BADGING_NONE, network.calculateBadge(TEST_RSSI));
+ }
+
+ private ScoredNetwork buildScoredNetworkWithGivenBadgeForTestRssi(int badge) {
+ RssiCurve badgingCurve =
+ new RssiCurve(RSSI_START, 10, new byte[] {0, 0, 0, 0, 0, 0, (byte) badge});
+ Bundle attr = new Bundle();
+ attr.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, badgingCurve);
+ return new ScoredNetwork(KEY, CURVE, false /* meteredHint */, attr);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
new file mode 100644
index 0000000..8a7b881
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ChooserWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+/**
+ * Chooser activity instrumentation tests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ChooserActivityTest {
+ @Rule
+ public ActivityTestRule<ChooserWrapperActivity> mActivityRule =
+ new ActivityTestRule<>(ChooserWrapperActivity.class, false,
+ false);
+
+ @Before
+ public void cleanOverrideData() {
+ sOverrides.reset();
+ }
+
+ @Test
+ public void customTitle() throws InterruptedException {
+ Intent sendIntent = createSendImageIntent();
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(null);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test"));
+ waitForIdle();
+ onView(withId(R.id.title)).check(matches(withText("chooser test")));
+ }
+
+ @Test
+ public void emptyTitle() throws InterruptedException {
+ sOverrides.isVoiceInteraction = false;
+ Intent sendIntent = createSendImageIntent();
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(null);
+ mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ onView(withId(R.id.title))
+ .check(matches(withText(R.string.whichSendApplication)));
+ }
+
+ @Test
+ public void twoOptionsAndUserSelectsOne() throws InterruptedException {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ assertThat(activity.getAdapter().getCount(), is(2));
+ onView(withId(R.id.profile_button)).check(matches(not(isDisplayed())));
+
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+ onView(withText(toChoose.activityInfo.name))
+ .perform(click());
+ waitForIdle();
+ assertThat(chosen[0], is(toChoose));
+ }
+
+ @Test
+ public void noResultsFromPackageManager() {
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(null);
+ Intent sendIntent = createSendImageIntent();
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+ assertThat(activity.isFinishing(), is(false));
+
+ onView(withId(R.id.empty)).check(matches(isDisplayed()));
+ onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed())));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> activity.getAdapter().handlePackagesChanged()
+ );
+ // backward compatibility. looks like we finish when data is empty after package change
+ assertThat(activity.isFinishing(), is(true));
+ }
+
+ @Test
+ public void autoLaunchSingleResult() throws InterruptedException {
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(1);
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ Intent sendIntent = createSendImageIntent();
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ assertThat(chosen[0], is(resolvedComponentInfos.get(0).getResolveInfoAt(0)));
+ assertThat(activity.isFinishing(), is(true));
+ }
+
+ private Intent createSendImageIntent() {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ sendIntent.setType("image/jpeg");
+ return sendIntent;
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ChooserDataProvider.createResolvedComponentInfo(i));
+ }
+ return infoList;
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java b/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
new file mode 100644
index 0000000..f6f63f1
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserDataProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.service.chooser.ChooserTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility class used by chooser tests to create mock data
+ */
+class ChooserDataProvider {
+
+ static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
+ return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
+ createResolverIntent(i), createResolveInfo(i));
+ }
+
+ static ComponentName createComponentName(int i) {
+ final String name = "component" + i;
+ return new ComponentName("foo.bar." + name, name);
+ }
+
+ static ResolveInfo createResolveInfo(int i) {
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.activityInfo = createActivityInfo(i);
+ resolveInfo.targetUserId = UserHandle.USER_CURRENT;
+ return resolveInfo;
+ }
+
+ static ActivityInfo createActivityInfo(int i) {
+ ActivityInfo ai = new ActivityInfo();
+ ai.name = "activity_name" + i;
+ ai.packageName = "foo_bar" + i;
+ ai.enabled = true;
+ ai.exported = true;
+ ai.permission = null;
+ ai.applicationInfo = createApplicationInfo();
+ return ai;
+ }
+
+ static ApplicationInfo createApplicationInfo() {
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.name = "app_name";
+ ai.packageName = "foo.bar";
+ ai.enabled = true;
+ return ai;
+ }
+
+ static Intent createResolverIntent(int i) {
+ return new Intent("intentAction" + i);
+ }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
new file mode 100644
index 0000000..66fb451
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.pm.PackageManager;
+
+import java.util.function.Function;
+
+import static org.mockito.Mockito.mock;
+
+
+/**
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
+public class ChooserWrapperActivity extends ChooserActivity {
+ static final OverrideData sOverrides = new OverrideData();
+
+ ResolveListAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public boolean isVoiceInteraction() {
+ if (sOverrides.isVoiceInteraction != null) {
+ return sOverrides.isVoiceInteraction;
+ }
+ return super.isVoiceInteraction();
+ }
+
+ @Override
+ public void safelyStartActivity(TargetInfo cti) {
+ if (sOverrides.onSafelyStartCallback != null &&
+ sOverrides.onSafelyStartCallback.apply(cti)) {
+ return;
+ }
+ super.safelyStartActivity(cti);
+ }
+
+ @Override
+ protected ResolverListController createListController() {
+ return sOverrides.resolverListController;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ if (sOverrides.createPackageManager != null) {
+ return sOverrides.createPackageManager.apply(super.getPackageManager());
+ }
+ return super.getPackageManager();
+ }
+
+ /**
+ * We cannot directly mock the activity created since instrumentation creates it.
+ * <p>
+ * Instead, we use static instances of this object to modify behavior.
+ */
+ static class OverrideData {
+ @SuppressWarnings("Since15")
+ public Function<PackageManager, PackageManager> createPackageManager;
+ public Function<TargetInfo, Boolean> onSafelyStartCallback;
+ public ResolverListController resolverListController;
+ public Boolean isVoiceInteraction;
+
+ public void reset() {
+ onSafelyStartCallback = null;
+ isVoiceInteraction = null;
+ createPackageManager = null;
+ resolverListController = mock(ResolverListController.class);
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index 9936489..1dc58e2 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -125,7 +125,10 @@
</dd>
</dl>
</li>
- <li><p>Enter additional information about the item, then click <strong>Save</strong>.</p>
+ <li><p>Enter additional information about the item. If you're adding a
+ subscription, also include
+ <a href="#billing-form-add-subscription">subscription-specific details</a>.
+ After you've provided this information, click <strong>Save</strong>.</p>
<dl>
<dt>Publishing State</dt>
<dd>
@@ -185,6 +188,73 @@
</figcaption>
</figure>
+<h4 id="billing-form-add-subscription">
+ Adding subscription details
+</h4>
+
+<p>
+ When you add a subscription to a product list, you must define its billing
+ period. All other settings related to subscriptions are optional. The
+ following list shows each property that you can set:
+</p>
+
+<dl>
+ <dt>Billing period</dt>
+ <dd>
+ <p>
+ Sets the frequency at which a user is charged while their subscription
+ is active.
+ </p>
+
+ <p>
+ If the billing period for the new subscription is Seasonal, you must
+ specify when the season starts and ends using the <strong>Start
+ date</strong> and <strong>End date</strong> options. The subscription is
+ active only between these two dates.
+ </p>
+
+ <p>
+ To allow users to activate a seasonal subscription for a discounted price
+ after the season begins, you can provide prorated pricing by selecting
+ <strong>Add Prorated Price</strong>. In the text boxes that appear after
+ you select this option, specify a discounted price for your subscription,
+ as well as the first date during the season when users can activate the
+ subscription for the discounted price. You can set multiple prorated
+ prices for a single subscription, with each price point taking effect on
+ a different start date.
+ </p>
+
+ <p class="note">
+ <strong>Note:</strong> After you activate a subscription, you cannot
+ change the subscription's billing period. For a seasonal subscription,
+ this restriction means that you also cannot change the season's start and
+ end dates. However, you can still add, change, and remove the prorated
+ prices that you've associated with a seasonal subscription.
+ </p>
+ </dd>
+
+ <dt>Free trial period</dt>
+ <dd>
+ Sets the number of days that users can access the subscription for free
+ after they first activate it. The trial period must be at least 7 days.
+ </dd>
+
+ <dt>Grace period</dt>
+ <dd>
+ Determines the amount of time that the user can continue accessing the
+ subscription after their payment is declined. If the user doesn't fix their
+ payment issue after the grace period has ended, their access to the
+ subscription is revoked.
+ </dd>
+</dl>
+
+<p>
+ To learn more about how you can manage subscriptions using the In-app Billing
+ service, see the
+ <a href="/google/play/billing/billing_subscriptions.html">In-app
+ Subscriptions</a> guide.
+</p>
+
<h3 id="billing-bulk-add">Adding a batch of items to a product list</h3>
<p>To add a batch of items to a product list using a CSV file, you first need to
diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd
index 44b7ad3..22480a2 100644
--- a/docs/html/google/play/billing/billing_testing.jd
+++ b/docs/html/google/play/billing/billing_testing.jd
@@ -199,6 +199,15 @@
with a reserved product ID. Figure 1 shows the checkout flow for the reserved item that has the
product ID android.test.purchased.</p>
+<p class="note">
+ <strong>Note:</strong> If you're testing subscription purchases, you must use
+ the product ID of an actual subscription, not a reserved product ID. To verify
+ that Google Play is processing the test subscription purchases correctly, you
+ should
+ <a href="/google/play/billing/billing/billing_admin.html#billing-testing-setup">set
+ up test accounts</a> for your app.
+</p>
+
<img src="{@docRoot}images/billing_test_flow.png" height="381" id="figure1" />
<p class="img-caption">
<strong>Figure 1.</strong>Purchase flow for the special reserved item android.test.purchased.
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index b6db327..a259937 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -372,6 +372,16 @@
}
/**
+ * This is called by methods that want to throw an exception if the bitmap
+ * is {@link Config#HARDWARE}.
+ */
+ private void checkHardware(String errorMessage) {
+ if (getConfig() == Config.HARDWARE) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ /**
* Common code for checking that x and y are >= 0
*
* @param x x coordinate to ensure is >= 0
@@ -512,8 +522,11 @@
* <p>After this method returns, the current position of the buffer is
* updated: the position is incremented by the number of elements written
* in the buffer.</p>
+ * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
*/
public void copyPixelsToBuffer(Buffer dst) {
+ checkHardware("unable to copyPixelsToBuffer, "
+ + "pixel access is not supported on Config#HARDWARE bitmaps");
int elements = dst.remaining();
int shift;
if (dst instanceof ByteBuffer) {
@@ -550,9 +563,11 @@
* updated: the position is incremented by the number of elements read from
* the buffer. If you need to read the bitmap from the buffer again you must
* first rewind the buffer.</p>
+ * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
*/
public void copyPixelsFromBuffer(Buffer src) {
checkRecycled("copyPixelsFromBuffer called on recycled bitmap");
+ checkHardware("unable to copyPixelsFromBuffer, Config#HARDWARE bitmaps are immutable");
int elements = src.remaining();
int shift;
@@ -753,6 +768,11 @@
return source;
}
+ boolean isHardware = source.getConfig() == Config.HARDWARE;
+ if (isHardware) {
+ source = nativeCopyPreserveInternalConfig(source.mNativePtr);
+ }
+
int neww = width;
int newh = height;
Canvas canvas = new Canvas();
@@ -824,7 +844,9 @@
canvas.setBitmap(bitmap);
canvas.drawBitmap(source, srcR, dstR, paint);
canvas.setBitmap(null);
-
+ if (isHardware) {
+ return bitmap.copy(Config.HARDWARE, false);
+ }
return bitmap;
}
@@ -1428,9 +1450,8 @@
@ColorInt
public int getPixel(int x, int y) {
checkRecycled("Can't call getPixel() on a recycled bitmap");
- if (getConfig() == Config.HARDWARE) {
- throw new IllegalStateException("Can't access pixels in hardware Bitmaps");
- }
+ checkHardware("unable to getPixel(), "
+ + "pixel access is not supported on Config#HARDWARE bitmaps");
checkPixelAccess(x, y);
return nativeGetPixel(mNativePtr, x, y);
}
@@ -1462,9 +1483,8 @@
public void getPixels(@ColorInt int[] pixels, int offset, int stride,
int x, int y, int width, int height) {
checkRecycled("Can't call getPixels() on a recycled bitmap");
- if (getConfig() == Config.HARDWARE) {
- throw new IllegalStateException("Can't access pixels in hardware Bitmaps");
- }
+ checkHardware("unable to getPixels(), "
+ + "pixel access is not supported on Config#HARDWARE bitmaps");
if (width == 0 || height == 0) {
return; // nothing to do
}
@@ -1773,4 +1793,5 @@
private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
private static native void nativePrepareToDraw(long nativeBitmap);
private static native int nativeGetAllocationByteCount(long nativeBitmap);
+ private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 761ee22..7033900 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -63,7 +63,7 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({MULTIPATH_INDICATOR_UNKNOWN, MULTIPATH_INDICATOR_DETECTED,
- MULTIPATH_INDICATOR_NOT_USED})
+ MULTIPATH_INDICATOR_NOT_DETECTED})
public @interface MultipathIndicator {}
/**
@@ -81,9 +81,6 @@
*/
public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
- /** @removed */
- public static final int MULTIPATH_INDICATOR_NOT_USED = 2;
-
/** This GNSS measurement's tracking state is invalid or unknown. */
public static final int STATE_UNKNOWN = 0;
/** This GNSS measurement's tracking state has code lock. */
@@ -803,8 +800,8 @@
return "Unknown";
case MULTIPATH_INDICATOR_DETECTED:
return "Detected";
- case MULTIPATH_INDICATOR_NOT_USED:
- return "NotUsed";
+ case MULTIPATH_INDICATOR_NOT_DETECTED:
+ return "NotDetected";
default:
return "<Invalid:" + mMultipathIndicator + ">";
}
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 7db0466..d66fd9c 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -34,13 +34,6 @@
* Events are delivered to registered instances of {@link Callback}.
*/
public final class GnssMeasurementsEvent implements Parcelable {
- /** @removed */
- public static final int STATUS_NOT_SUPPORTED = 0;
- /** @removed */
- public static final int STATUS_READY = 1;
- /** @removed */
- public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
-
private final GnssClock mClock;
private final Collection<GnssMeasurement> mReadOnlyMeasurements;
diff --git a/location/java/android/location/GnssNavigationMessageEvent.java b/location/java/android/location/GnssNavigationMessageEvent.java
deleted file mode 100644
index f7e5665..0000000
--- a/location/java/android/location/GnssNavigationMessageEvent.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2014 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.location;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * A class implementing a container for data associated with a navigation message event.
- * Events are delivered to registered instances of {@link Callback}.
- * @removed
- */
-public final class GnssNavigationMessageEvent implements Parcelable {
- /**
- * The status of GNSS measurements event.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_GNSS_LOCATION_DISABLED})
- public @interface GnssNavigationMessageStatus {}
-
- /**
- * The system does not support tracking of GNSS Navigation Messages.
- *
- * This status will not change in the future.
- */
- public static final int STATUS_NOT_SUPPORTED = 0;
-
- /**
- * GNSS Navigation Messages are successfully being tracked, it will receive updates once they
- * are available.
- */
- public static final int STATUS_READY = 1;
-
- /**
- * GNSS provider or Location is disabled, updated will not be received until they are enabled.
- */
- public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
-
- private final GnssNavigationMessage mNavigationMessage;
-
- /**
- * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
- *
- * <p>You can implement this interface and call
- * {@link LocationManager#registerGnssNavigationMessageCallback}.
- */
- public static abstract class Callback {
-
- /**
- * Returns the latest collected GNSS Navigation Message.
- */
- public void onGnssNavigationMessageReceived(GnssNavigationMessageEvent event) {}
-
- /**
- * Returns the latest status of the GNSS Navigation Messages sub-system.
- */
- public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
- }
-
- public GnssNavigationMessageEvent(GnssNavigationMessage message) {
- if (message == null) {
- throw new InvalidParameterException("Parameter 'message' must not be null.");
- }
- mNavigationMessage = message;
- }
-
- @NonNull
- public GnssNavigationMessage getNavigationMessage() {
- return mNavigationMessage;
- }
-
- public static final Creator<GnssNavigationMessageEvent> CREATOR =
- new Creator<GnssNavigationMessageEvent>() {
- @Override
- public GnssNavigationMessageEvent createFromParcel(Parcel in) {
- ClassLoader classLoader = getClass().getClassLoader();
- GnssNavigationMessage navigationMessage = in.readParcelable(classLoader);
- return new GnssNavigationMessageEvent(navigationMessage);
- }
-
- @Override
- public GnssNavigationMessageEvent[] newArray(int size) {
- return new GnssNavigationMessageEvent[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeParcelable(mNavigationMessage, flags);
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder("[ GnssNavigationMessageEvent:\n\n");
- builder.append(mNavigationMessage.toString());
- builder.append("\n]");
- return builder.toString();
- }
-}
diff --git a/location/java/android/location/GnssNmeaListener.java b/location/java/android/location/GnssNmeaListener.java
deleted file mode 100644
index 756ae49..0000000
--- a/location/java/android/location/GnssNmeaListener.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.location;
-
-/**
-* Used for receiving NMEA sentences from the GNSS.
-* NMEA 0183 is a standard for communicating with marine electronic devices
-* and is a common method for receiving data from a GNSS, typically over a serial port.
-* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
-* You can implement this interface and call {@link LocationManager#addNmeaListener}
-* to receive NMEA data from the GNSS engine.
-* @removed
-*/
-public interface GnssNmeaListener {
- /** Called when an NMEA message is received. */
- void onNmeaReceived(long timestamp, String nmea);
-}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index e68821b..f105144 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -111,11 +111,6 @@
mAzimuths = azimuths;
}
- /** @removed */
- public int getNumSatellites() {
- return getSatelliteCount();
- }
-
/**
* Gets the total number of satellites in satellite list.
*/
@@ -191,11 +186,6 @@
return mAzimuths[satIndex];
}
- /** @removed */
- public boolean hasEphemeris(int satIndex) {
- return hasEphemerisData(satIndex);
- }
-
/**
* Reports whether the satellite at the specified index has ephemeris data.
*
@@ -205,11 +195,6 @@
return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
}
- /** @removed */
- public boolean hasAlmanac(int satIndex) {
- return hasAlmanacData(satIndex);
- }
-
/**
* Reports whether the satellite at the specified index has almanac data.
*
diff --git a/location/java/android/location/GnssStatusCallback.java b/location/java/android/location/GnssStatusCallback.java
deleted file mode 100644
index bf295ef..0000000
--- a/location/java/android/location/GnssStatusCallback.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.location;
-
-/**
- * Used for receiving notifications when GNSS events happen.
- * @removed
- */
-public abstract class GnssStatusCallback {
- /**
- * Called when GNSS system has started.
- */
- public void onStarted() {}
-
- /**
- * Called when GNSS system has stopped.
- */
- public void onStopped() {}
-
- /**
- * Called when the GNSS system has received its first fix since starting.
- * @param ttffMillis the time from start to first fix in milliseconds.
- */
- public void onFirstFix(int ttffMillis) {}
-
- /**
- * Called periodically to report GNSS satellite status.
- * @param status the current status of all satellites.
- */
- public void onSatelliteStatusChanged(GnssStatus status) {}
-}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index da0e515..087e74f 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -70,16 +70,10 @@
new HashMap<>();
private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
new HashMap<>();
- private final HashMap<GnssStatusCallback, GnssStatusListenerTransport>
- mOldGnssStatusListeners = new HashMap<>();
private final HashMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
new HashMap<>();
- private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mOldGnssNmeaListeners =
- new HashMap<>();
private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
new HashMap<>();
- private final HashMap<GnssNavigationMessageEvent.Callback, GnssNavigationMessage.Callback>
- mNavigationMessageBridge = new HashMap<>();
private GnssStatus mGnssStatus;
private int mTimeToFirstFix;
@@ -1398,9 +1392,7 @@
private final GpsStatus.Listener mGpsListener;
private final GpsStatus.NmeaListener mGpsNmeaListener;
- private final GnssStatusCallback mOldGnssCallback;
private final GnssStatus.Callback mGnssCallback;
- private final GnssNmeaListener mOldGnssNmeaListener;
private final OnNmeaMessageListener mGnssNmeaListener;
private class GnssHandler extends Handler {
@@ -1464,7 +1456,6 @@
mGnssHandler = new GnssHandler(handler);
mGpsNmeaListener = null;
mNmeaBuffer = null;
- mOldGnssCallback = null;
mGnssCallback = mGpsListener != null ? new GnssStatus.Callback() {
@Override
public void onStarted() {
@@ -1486,7 +1477,6 @@
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
}
} : null;
- mOldGnssNmeaListener = null;
mGnssNmeaListener = null;
}
@@ -1499,9 +1489,7 @@
mGnssHandler = new GnssHandler(handler);
mGpsNmeaListener = listener;
mNmeaBuffer = new ArrayList<Nmea>();
- mOldGnssCallback = null;
mGnssCallback = null;
- mOldGnssNmeaListener = null;
mGnssNmeaListener = mGpsNmeaListener != null ? new OnNmeaMessageListener() {
@Override
public void onNmeaMessage(String nmea, long timestamp) {
@@ -1510,85 +1498,26 @@
} : null;
}
- GnssStatusListenerTransport(GnssStatusCallback callback) {
- this(callback, null);
- }
-
- GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
- mOldGnssCallback = callback;
- mGnssCallback = mOldGnssCallback != null ? new GnssStatus.Callback() {
- @Override
- public void onStarted() {
- mOldGnssCallback.onStarted();
- }
-
- @Override
- public void onStopped() {
- mOldGnssCallback.onStopped();
- }
-
- @Override
- public void onFirstFix(int ttff) {
- mOldGnssCallback.onFirstFix(ttff);
- }
-
- @Override
- public void onSatelliteStatusChanged(GnssStatus status) {
- mOldGnssCallback.onSatelliteStatusChanged(status);
- }
- } : null;
- mGnssHandler = new GnssHandler(handler);
- mOldGnssNmeaListener = null;
- mGnssNmeaListener = null;
- mNmeaBuffer = null;
- mGpsListener = null;
- mGpsNmeaListener = null;
- }
-
GnssStatusListenerTransport(GnssStatus.Callback callback) {
this(callback, null);
}
GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
- mOldGnssCallback = null;
mGnssCallback = callback;
mGnssHandler = new GnssHandler(handler);
- mOldGnssNmeaListener = null;
mGnssNmeaListener = null;
mNmeaBuffer = null;
mGpsListener = null;
mGpsNmeaListener = null;
}
- GnssStatusListenerTransport(GnssNmeaListener listener) {
- this(listener, null);
- }
-
- GnssStatusListenerTransport(GnssNmeaListener listener, Handler handler) {
- mGnssCallback = null;
- mOldGnssCallback = null;
- mGnssHandler = new GnssHandler(handler);
- mOldGnssNmeaListener = listener;
- mGnssNmeaListener = mOldGnssNmeaListener != null ? new OnNmeaMessageListener() {
- @Override
- public void onNmeaMessage(String message, long timestamp) {
- mOldGnssNmeaListener.onNmeaReceived(timestamp, message);
- }
- } : null;
- mGpsListener = null;
- mGpsNmeaListener = null;
- mNmeaBuffer = new ArrayList<Nmea>();
- }
-
GnssStatusListenerTransport(OnNmeaMessageListener listener) {
this(listener, null);
}
GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
- mOldGnssCallback = null;
mGnssCallback = null;
mGnssHandler = new GnssHandler(handler);
- mOldGnssNmeaListener = null;
mGnssNmeaListener = listener;
mGpsListener = null;
mGpsNmeaListener = null;
@@ -1703,73 +1632,9 @@
}
/**
- * Registers a GNSS status listener.
+ * Registers a GNSS status callback.
*
- * @param callback GNSS status listener object to register
- *
- * @return true if the listener was successfully added
- *
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- * @removed
- */
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public boolean registerGnssStatusCallback(GnssStatusCallback callback) {
- return registerGnssStatusCallback(callback, null);
- }
-
- /**
- * Registers a GNSS status listener.
- *
- * @param callback GNSS status listener object to register
- * @param handler the handler that the callback runs on.
- *
- * @return true if the listener was successfully added
- *
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- * @removed
- */
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public boolean registerGnssStatusCallback(GnssStatusCallback callback, Handler handler) {
- boolean result;
- if (mOldGnssStatusListeners.get(callback) != null) {
- // listener is already registered
- return true;
- }
- try {
- GnssStatusListenerTransport transport =
- new GnssStatusListenerTransport(callback, handler);
- result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
- if (result) {
- mOldGnssStatusListeners.put(callback, transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- return result;
- }
-
- /**
- * Removes a GNSS status listener.
- *
- * @param callback GNSS status listener object to remove
- * @removed
- */
- public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
- try {
- GnssStatusListenerTransport transport = mOldGnssStatusListeners.remove(callback);
- if (transport != null) {
- mService.unregisterGnssStatusCallback(transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Registers a GNSS status listener.
- *
- * @param callback GNSS status listener object to register
+ * @param callback GNSS status callback object to register
*
* @return true if the listener was successfully added
*
@@ -1781,9 +1646,9 @@
}
/**
- * Registers a GNSS status listener.
+ * Registers a GNSS status callback.
*
- * @param callback GNSS status listener object to register
+ * @param callback GNSS status callback object to register
* @param handler the handler that the callback runs on.
*
* @return true if the listener was successfully added
@@ -1812,9 +1677,9 @@
}
/**
- * Removes a GNSS status listener.
+ * Removes a GNSS status callback.
*
- * @param callback GNSS status listener object to remove
+ * @param callback GNSS status callback object to remove
*/
public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
try {
@@ -1880,71 +1745,6 @@
/**
* Adds an NMEA listener.
*
- * @param listener a {@link GnssNmeaListener} object to register
- *
- * @return true if the listener was successfully added
- *
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- * @removed
- */
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public boolean addNmeaListener(GnssNmeaListener listener) {
- return addNmeaListener(listener, null);
- }
-
- /**
- * Adds an NMEA listener.
- *
- * @param listener a {@link GnssNmeaListener} object to register
- * @param handler the handler that the listener runs on.
- *
- * @return true if the listener was successfully added
- *
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- * @removed
- */
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public boolean addNmeaListener(GnssNmeaListener listener, Handler handler) {
- boolean result;
-
- if (mGpsNmeaListeners.get(listener) != null) {
- // listener is already registered
- return true;
- }
- try {
- GnssStatusListenerTransport transport =
- new GnssStatusListenerTransport(listener, handler);
- result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
- if (result) {
- mOldGnssNmeaListeners.put(listener, transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- return result;
- }
-
- /**
- * Removes an NMEA listener.
- *
- * @param listener a {@link GnssNmeaListener} object to remove
- * @removed
- */
- public void removeNmeaListener(GnssNmeaListener listener) {
- try {
- GnssStatusListenerTransport transport = mOldGnssNmeaListeners.remove(listener);
- if (transport != null) {
- mService.unregisterGnssStatusCallback(transport);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Adds an NMEA listener.
- *
* @param listener a {@link OnNmeaMessageListener} object to register
*
* @return true if the listener was successfully added
@@ -2088,58 +1888,6 @@
/**
* Registers a GNSS Navigation Message callback.
*
- * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
- * @return {@code true} if the callback was added successfully, {@code false} otherwise.
- * @removed
- */
- public boolean registerGnssNavigationMessageCallback(
- GnssNavigationMessageEvent.Callback callback) {
- return registerGnssNavigationMessageCallback(callback, null);
- }
-
- /**
- * Registers a GNSS Navigation Message callback.
- *
- * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
- * @param handler the handler that the callback runs on.
- * @return {@code true} if the callback was added successfully, {@code false} otherwise.
- * @removed
- */
- @RequiresPermission(ACCESS_FINE_LOCATION)
- public boolean registerGnssNavigationMessageCallback(
- final GnssNavigationMessageEvent.Callback callback, Handler handler) {
- GnssNavigationMessage.Callback bridge = new GnssNavigationMessage.Callback() {
- @Override
- public void onGnssNavigationMessageReceived(GnssNavigationMessage message) {
- GnssNavigationMessageEvent event = new GnssNavigationMessageEvent(message);
- callback.onGnssNavigationMessageReceived(event);
- }
-
- @Override
- public void onStatusChanged(int status) {
- callback.onStatusChanged(status);
- }
- };
- mNavigationMessageBridge.put(callback, bridge);
- return mGnssNavigationMessageCallbackTransport.add(bridge, handler);
- }
-
- /**
- * Unregisters a GNSS Navigation Message callback.
- *
- * @param callback a {@link GnssNavigationMessageEvent.Callback} object to remove.
- * @removed
- */
- public void unregisterGnssNavigationMessageCallback(
- GnssNavigationMessageEvent.Callback callback) {
- mGnssNavigationMessageCallbackTransport.remove(
- mNavigationMessageBridge.remove(
- callback));
- }
-
- /**
- * Registers a GNSS Navigation Message callback.
- *
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
*/
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 23bb6f9..1d124c5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3482,6 +3482,20 @@
return delay;
}
+ /**
+ * Indicate A2DP device configuration has changed.
+ * @param device Bluetooth device whose configuration has changed.
+ * {@hide}
+ */
+ public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) {
+ IAudioService service = getService();
+ try {
+ service.handleBluetoothA2dpDeviceConfigChange(device);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** {@hide} */
public IRingtonePlayer getRingtonePlayer() {
try {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 17928995..b44a710 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -689,6 +689,9 @@
public static native int setDeviceConnectionState(int device, int state,
String device_address, String device_name);
public static native int getDeviceConnectionState(int device, String device_address);
+ public static native int handleDeviceConfigChange(int device,
+ String device_address,
+ String device_name);
public static native int setPhoneState(int state);
public static native int setForceUse(int usage, int config);
public static native int getForceUse(int usage);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d53c937..084675f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -143,6 +143,8 @@
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state, int profile);
+ void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device);
+
AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
boolean isCameraSoundForced();
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 77a0fbd..500556a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2291,7 +2291,7 @@
* {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
*/
private synchronized void setSubtitleAnchor() {
- if (mSubtitleController == null) {
+ if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
@@ -2442,10 +2442,7 @@
}
private void scanInternalSubtitleTracks() {
- if (mSubtitleController == null) {
- Log.d(TAG, "setSubtitleAnchor in MediaPlayer");
- setSubtitleAnchor();
- }
+ setSubtitleAnchor();
populateInbandTracks();
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index a68a44e..abe310a 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -18,7 +18,7 @@
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?android:attr/colorPrimaryDark">
+ android:background="?android:attr/statusBarColor">
<!-- The main content view -->
<LinearLayout
android:id="@+id/content_parent"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5c2787b..5574753 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -427,6 +427,7 @@
android:supportsPictureInPicture="true"
android:stateNotNeeded="true"
android:taskAffinity=""
+ android:launchMode="singleTop"
androidprv:alwaysFocusable="true" />
<!-- platform logo easter egg activity -->
diff --git a/packages/SystemUI/res/color/tint_color_selector.xml b/packages/SystemUI/res/color/tint_color_selector.xml
new file mode 100644
index 0000000..cb24425
--- /dev/null
+++ b/packages/SystemUI/res/color/tint_color_selector.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="0.25"
+ android:color="?android:attr/colorForeground"/>
+ <item android:state_activated="false"
+ android:alpha="0.3"
+ android:color="?android:attr/colorForeground"/>
+ <item android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
index 736cfd8..83f46e5 100644
--- a/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
+++ b/packages/SystemUI/res/drawable/ic_hotspot_unavailable.xml
@@ -20,7 +20,8 @@
android:width="48dp"
android:viewportHeight="48"
android:viewportWidth="48"
- android:tint="@color/qs_tile_tint_unavailable" >
+ android:alpha="0.25"
+ android:tint="?android:attr/colorControlNormal" >
<group
android:name="ic_hotspot"
android:translateX="23.97354"
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 054bfab..cadc09b 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -19,7 +19,7 @@
android:id="@+id/menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#33000000">
+ android:background="#66000000">
<LinearLayout
android:id="@+id/actions"
@@ -45,7 +45,7 @@
android:text="@string/pip_phone_dismiss"
android:background="?android:selectableItemBackground" />
<TextView
- android:id="@+id/minimize"
+ android:id="@+id/expand"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -53,7 +53,7 @@
android:textSize="12sp"
android:textColor="#ffffffff"
android:fontFamily="sans-serif"
- android:text="@string/pip_phone_minimize"
+ android:text="@string/pip_phone_expand"
android:background="?android:selectableItemBackground" />
</LinearLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index c7149df..ba8c644 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -144,10 +144,6 @@
<color name="remote_input_accent">#eeeeee</color>
- <color name="qs_tile_tint_unavailable">#40ffffff</color>
- <color name="qs_tile_tint_inactive">#4dffffff</color>
- <color name="qs_tile_tint_active">#ffffffff</color>
-
<!-- Keyboard shortcuts colors -->
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fc1d816..614472a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -68,7 +68,7 @@
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
- <item name="android:windowBackground">@drawable/forced_resizable_background</item>
+ <item name="android:windowBackground">@null</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:statusBarColor">@color/transparent</item>
<item name="android:windowAnimationStyle">@style/Animation.PipPhoneOverlayControl</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index c4698c3..07ef5e0 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -491,6 +491,12 @@
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ mFramePaint.setColorFilter(colorFilter);
+ mBatteryPaint.setColorFilter(colorFilter);
+ mWarningTextPaint.setColorFilter(colorFilter);
+ mTextPaint.setColorFilter(colorFilter);
+ mBoltPaint.setColorFilter(colorFilter);
+ mPlusPaint.setColorFilter(colorFilter);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index fe8ee6f..e4f7816 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -16,13 +16,16 @@
package com.android.systemui.pip.phone;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
+import android.graphics.PointF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -30,11 +33,15 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import java.util.ArrayList;
@@ -47,28 +54,40 @@
private static final String TAG = "PipMenuActivity";
- public static final int MESSAGE_FINISH_SELF = 1;
- public static final int MESSAGE_UPDATE_ACTIONS = 2;
+ public static final int MESSAGE_SHOW_MENU = 1;
+ public static final int MESSAGE_HIDE_MENU = 2;
+ public static final int MESSAGE_UPDATE_ACTIONS = 3;
private static final long INITIAL_DISMISS_DELAY = 2000;
private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
+ private static final long MENU_FADE_DURATION = 125;
- private List<RemoteAction> mActions = new ArrayList<>();
+ private boolean mMenuVisible;
+ private final List<RemoteAction> mActions = new ArrayList<>();
+ private View mMenuContainer;
private View mDismissButton;
- private View mMinimizeButton;
+ private View mExpandButton;
+ private ObjectAnimator mMenuContainerAnimator;
+
+ private PointF mDownPosition = new PointF();
+ private PointF mDownDelta = new PointF();
+ private ViewConfiguration mViewConfig;
private Handler mHandler = new Handler();
private Messenger mToControllerMessenger;
private Messenger mMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MESSAGE_SHOW_MENU:
+ showMenu();
+ break;
+ case MESSAGE_HIDE_MENU:
+ hideMenu();
+ break;
case MESSAGE_UPDATE_ACTIONS:
setActions(((ParceledListSlice) msg.obj).getList());
break;
- case MESSAGE_FINISH_SELF:
- finish();
- break;
}
}
});
@@ -76,12 +95,17 @@
private final Runnable mFinishRunnable = new Runnable() {
@Override
public void run() {
- finish();
+ hideMenu();
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
+ // Set the flags to allow us to watch for outside touches and also hide the menu and start
+ // manipulating the PIP in the same touch gesture
+ mViewConfig = ViewConfiguration.get(this);
+ getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY);
+
super.onCreate(savedInstanceState);
setContentView(R.layout.pip_menu_activity);
@@ -94,44 +118,74 @@
setActions(actions.getList());
}
- findViewById(R.id.menu).setOnClickListener((v) -> {
+ mMenuContainer = findViewById(R.id.menu);
+ mMenuContainer.setOnClickListener((v) -> {
expandPip();
});
mDismissButton = findViewById(R.id.dismiss);
mDismissButton.setOnClickListener((v) -> {
dismissPip();
});
- mMinimizeButton = findViewById(R.id.minimize);
- mMinimizeButton.setOnClickListener((v) -> {
- minimizePip();
+ mExpandButton = findViewById(R.id.expand);
+ mExpandButton.setOnClickListener((v) -> {
+ expandPip();
});
+
+ notifyActivityCallback(mMessenger);
+ showMenu();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ showMenu();
}
@Override
protected void onStart() {
super.onStart();
- notifyActivityVisibility(true);
+ notifyMenuVisibility(true);
repostDelayedFinish(INITIAL_DISMISS_DELAY);
}
@Override
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
+ if (!isInPictureInPictureMode) {
+ finish();
+ }
+ }
+
+ @Override
public void onUserInteraction() {
repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
}
@Override
- protected void onStop() {
- super.onStop();
- finish();
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ // On the first action outside the window, hide the menu
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_OUTSIDE:
+ hideMenu();
+ break;
+ case MotionEvent.ACTION_DOWN:
+ mDownPosition.set(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
+ if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() && mMenuVisible) {
+ hideMenu();
+ mMenuVisible = false;
+ }
+ }
+ return super.dispatchTouchEvent(ev);
}
@Override
public void finish() {
- View v = getWindow().getDecorView();
- v.removeCallbacks(mFinishRunnable);
- notifyActivityVisibility(false);
+ notifyActivityCallback(null);
super.finish();
- overridePendingTransition(0, R.anim.forced_resizable_exit);
+ // Hide without an animation (the menu should already be invisible at this point)
+ overridePendingTransition(0, 0);
}
@Override
@@ -139,6 +193,51 @@
// Do nothing
}
+ private void showMenu() {
+ if (!mMenuVisible) {
+ if (mMenuContainerAnimator != null) {
+ mMenuContainerAnimator.cancel();
+ }
+
+ notifyMenuVisibility(true);
+ mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+ mMenuContainer.getAlpha(), 1f);
+ mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+ mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ repostDelayedFinish(INITIAL_DISMISS_DELAY);
+ }
+ });
+ mMenuContainerAnimator.start();
+ }
+ }
+
+ private void hideMenu() {
+ hideMenu(null /* animationFinishedRunnable */);
+ }
+
+ private void hideMenu(final Runnable animationFinishedRunnable) {
+ if (mMenuVisible) {
+ cancelDelayedFinish();
+ notifyMenuVisibility(false);
+ mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+ mMenuContainer.getAlpha(), 0f);
+ mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+ mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
+ mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (animationFinishedRunnable != null) {
+ animationFinishedRunnable.run();
+ }
+ }
+ });
+ mMenuContainerAnimator.start();
+ }
+ }
+
private void setActions(List<RemoteAction> actions) {
mActions.clear();
mActions.addAll(actions);
@@ -173,17 +272,19 @@
}
}
- private void notifyActivityVisibility(boolean visible) {
+ private void notifyMenuVisibility(boolean visible) {
+ mMenuVisible = visible;
Message m = Message.obtain();
- m.what = PipMenuActivityController.MESSAGE_ACTIVITY_VISIBILITY_CHANGED;
+ m.what = PipMenuActivityController.MESSAGE_MENU_VISIBILITY_CHANGED;
m.arg1 = visible ? 1 : 0;
- m.replyTo = visible ? mMessenger : null;
sendMessage(m, "Could not notify controller of PIP menu visibility");
}
private void expandPip() {
- sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
- "Could not notify controller to expand PIP");
+ hideMenu(() -> {
+ sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
+ "Could not notify controller to expand PIP");
+ });
}
private void minimizePip() {
@@ -192,8 +293,17 @@
}
private void dismissPip() {
- sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
- "Could not notify controller to dismiss PIP");
+ hideMenu(() -> {
+ sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
+ "Could not notify controller to dismiss PIP");
+ });
+ }
+
+ private void notifyActivityCallback(Messenger callback) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
+ m.replyTo = callback;
+ sendMessage(m, "Could not notify controller of activity finished");
}
private void sendEmptyMessage(int what, String errorMsg) {
@@ -210,6 +320,11 @@
}
}
+ private void cancelDelayedFinish() {
+ View v = getWindow().getDecorView();
+ v.removeCallbacks(mFinishRunnable);
+ }
+
private void repostDelayedFinish(long delay) {
View v = getWindow().getDecorView();
v.removeCallbacks(mFinishRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 64e2d1a..0350cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -25,10 +25,11 @@
public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
public static final String EXTRA_ACTIONS = "actions";
- public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 100;
+ public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
public static final int MESSAGE_EXPAND_PIP = 101;
public static final int MESSAGE_MINIMIZE_PIP = 102;
public static final int MESSAGE_DISMISS_PIP = 103;
+ public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
/**
* A listener interface to receive notification on changes in PIP.
@@ -67,34 +68,25 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_ACTIVITY_VISIBILITY_CHANGED: {
+ case MESSAGE_MENU_VISIBILITY_CHANGED: {
boolean visible = msg.arg1 > 0;
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipMenuVisibilityChanged(visible);
- }
- mToActivityMessenger = msg.replyTo;
+ mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
break;
}
case MESSAGE_EXPAND_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipExpand();
- }
+ mListeners.forEach(l -> l.onPipExpand());
break;
}
case MESSAGE_MINIMIZE_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipMinimize();
- }
+ mListeners.forEach(l -> l.onPipMinimize());
break;
}
case MESSAGE_DISMISS_PIP: {
- int listenerCount = mListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- mListeners.get(i).onPipDismiss();
- }
+ mListeners.forEach(l -> l.onPipDismiss());
+ break;
+ }
+ case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
+ mToActivityMessenger = msg.replyTo;
break;
}
}
@@ -121,24 +113,34 @@
* Shows the menu activity.
*/
public void showMenu() {
- // Start the menu activity on the top task of the pinned stack
- try {
- StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
- pinnedStackInfo.taskIds.length > 0) {
- Intent intent = new Intent(mContext, PipMenuActivity.class);
- intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
- intent.putExtra(EXTRA_ACTIONS, mActions);
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchTaskId(
- pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
- options.setTaskOverlay(true);
- mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
- } else {
- Log.e(TAG, "No PIP tasks found");
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify menu to show", e);
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error showing PIP menu activity", e);
+ } else {
+ // Start the menu activity on the top task of the pinned stack
+ try {
+ StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+ pinnedStackInfo.taskIds.length > 0) {
+ Intent intent = new Intent(mContext, PipMenuActivity.class);
+ intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+ intent.putExtra(EXTRA_ACTIONS, mActions);
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+ options.setLaunchTaskId(
+ pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+ options.setTaskOverlay(true);
+ mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+ } else {
+ Log.e(TAG, "No PIP tasks found");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error showing PIP menu activity", e);
+ }
}
}
@@ -148,13 +150,12 @@
public void hideMenu() {
if (mToActivityMessenger != null) {
Message m = Message.obtain();
- m.what = PipMenuActivity.MESSAGE_FINISH_SELF;
+ m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
try {
mToActivityMessenger.send(m);
} catch (RemoteException e) {
- Log.e(TAG, "Could not notify menu activity to finish", e);
+ Log.e(TAG, "Could not notify menu to hide", e);
}
- mToActivityMessenger = null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index ff3cc79..380e4683 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -174,7 +174,7 @@
@Override
public void onPipDismiss() {
- animateDismissPinnedStack(mPinnedStackBounds);
+ BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
}
}
@@ -328,26 +328,31 @@
* Registers the input consumer.
*/
private void registerInputConsumer() {
- final InputChannel inputChannel = new InputChannel();
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to create PIP input consumer", e);
+ if (mInputEventReceiver == null) {
+ final InputChannel inputChannel = new InputChannel();
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to create PIP input consumer", e);
+ }
+ mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
}
- mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
}
/**
* Unregisters the input consumer.
*/
private void unregisterInputConsumer() {
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ if (mInputEventReceiver != null) {
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ }
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
}
- mInputEventReceiver.dispose();
}
/**
@@ -761,10 +766,6 @@
private PipTouchGesture mTapThroughGesture = new PipTouchGesture() {
@Override
boolean onMove(PipTouchState touchState) {
- if (mEnableTapThrough && touchState.startedDragging()) {
- mIsTappingThrough = false;
- mMenuController.hideMenu();
- }
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index dc42adc..fdefcf9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -362,6 +362,11 @@
private final TouchAnimator.Listener mNonFirstPageListener =
new TouchAnimator.ListenerAdapter() {
@Override
+ public void onAnimationAtEnd() {
+ mQuickQsPanel.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
public void onAnimationStarted() {
mQuickQsPanel.setVisibility(View.VISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 1fcb45b..9e3889b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -15,6 +15,9 @@
*/
package com.android.systemui.qs.external;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -24,7 +27,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
@@ -37,19 +39,14 @@
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.IWindowManager;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
import com.android.systemui.statusbar.phone.QSTileHost;
import libcore.util.Objects;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-
public class CustomTile extends QSTile<QSTile.State> implements TileChangeListener {
public static final String PREFIX = "custom(";
@@ -301,7 +298,7 @@
tileState = Tile.STATE_UNAVAILABLE;
drawable = mDefaultIcon.loadDrawable(mAppContext);
}
- int color = mContext.getColor(getColor(tileState));
+ final int color = TileColorPicker.getInstance(mContext).getColor(tileState);
drawable.setTint(color);
state.icon = mHasRes ? new DrawableIconWithRes(drawable, icon.getResId())
: new DrawableIcon(drawable);
@@ -335,18 +332,6 @@
});
}
- private static int getColor(int state) {
- switch (state) {
- case Tile.STATE_UNAVAILABLE:
- return R.color.qs_tile_tint_unavailable;
- case Tile.STATE_INACTIVE:
- return R.color.qs_tile_tint_inactive;
- case Tile.STATE_ACTIVE:
- return R.color.qs_tile_tint_active;
- }
- return 0;
- }
-
public static String toSpec(ComponentName name) {
return PREFIX + name.flattenToShortString() + ")";
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
new file mode 100644
index 0000000..0cc17ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileColorPicker.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.annotation.VisibleForTesting;
+import com.android.systemui.R;
+
+public class TileColorPicker {
+ @VisibleForTesting static final int[] DISABLE_STATE_SET = {-android.R.attr.state_enabled};
+ @VisibleForTesting static final int[] ENABLE_STATE_SET = {android.R.attr.state_enabled,
+ android.R.attr.state_activated};
+ @VisibleForTesting static final int[] INACTIVE_STATE_SET = {-android.R.attr.state_activated};
+ private static TileColorPicker sInstance;
+
+ private ColorStateList mColorStateList;
+
+ private TileColorPicker(Context context) {
+ mColorStateList = context.getResources().
+ getColorStateList(R.color.tint_color_selector, context.getTheme());
+ }
+
+ public static TileColorPicker getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new TileColorPicker(context);
+ }
+ return sInstance;
+ }
+
+ public int getColor(int state) {
+ final int defaultColor = 0;
+
+ switch (state) {
+ case Tile.STATE_UNAVAILABLE:
+ return mColorStateList.getColorForState(DISABLE_STATE_SET, defaultColor);
+ case Tile.STATE_INACTIVE:
+ return mColorStateList.getColorForState(INACTIVE_STATE_SET, defaultColor);
+ case Tile.STATE_ACTIVE:
+ return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+ default:
+ return mColorStateList.getColorForState(ENABLE_STATE_SET, defaultColor);
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index fc1c1ac..87b00a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.RelativeSizeSpan;
@@ -40,6 +43,7 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS.DetailAdapter;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.BatteryController;
import java.text.NumberFormat;
@@ -120,6 +124,8 @@
context.getColor(R.color.batterymeter_frame_color));
drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
drawable.onPowerSaveChanged(mPowerSave);
+ final int color = TileColorPicker.getInstance(context).getColor(Tile.STATE_ACTIVE);
+ drawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
return drawable;
}
@@ -205,6 +211,11 @@
mDrawable.onBatteryLevelChanged(100, false, false);
mDrawable.onPowerSaveChanged(true);
mDrawable.disableShowPercent();
+
+ final int color = TileColorPicker.getInstance(mCurrentView.getContext())
+ .getColor(Tile.STATE_ACTIVE);
+ mDrawable.setColorFilter(new PorterDuffColorFilter(color, Mode.SRC_IN));
+
((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
checkbox.setChecked(mPowerSave);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5b1638f..d86aebf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.provider.MediaStore;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.widget.Switch;
@@ -28,6 +29,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.FlashlightController;
/** Quick settings tile: Control flashlight **/
@@ -104,7 +106,8 @@
if (!mFlashlightController.isAvailable()) {
Drawable icon = mHost.getContext().getDrawable(R.drawable.ic_signal_flashlight_disable)
.mutate();
- final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+ final int disabledColor = TileColorPicker.getInstance(mContext)
+ .getColor(Tile.STATE_UNAVAILABLE);
icon.setTint(disabledColor);
state.icon = new DrawableIcon(icon);
state.label = new SpannableStringBuilder().append(state.label,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index dcee659..99485bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -22,6 +22,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.widget.Switch;
@@ -31,6 +32,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.TileColorPicker;
import com.android.systemui.statusbar.policy.HotspotController;
/** Quick settings tile: Hotspot **/
@@ -124,7 +126,8 @@
boolean wasAirplane = state.isAirplaneMode;
state.isAirplaneMode = mAirplaneMode.getValue() != 0;
if (state.isAirplaneMode) {
- final int disabledColor = mHost.getContext().getColor(R.color.qs_tile_tint_unavailable);
+ final int disabledColor = TileColorPicker.getInstance(mContext)
+ .getColor(Tile.STATE_UNAVAILABLE);
state.label = new SpannableStringBuilder().append(state.label,
new ForegroundColorSpan(disabledColor),
SpannableStringBuilder.SPAN_INCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
new file mode 100644
index 0000000..ba451e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileColorPickerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.external;
+
+import android.content.res.ColorStateList;
+import android.service.quicksettings.Tile;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.systemui.R;
+
+import static junit.framework.Assert.assertEquals;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TileColorPickerTest extends SysuiTestCase {
+ private static final int DEFAULT_COLOR = 0;
+
+ private TileColorPicker mTileColorPicker;
+ private ColorStateList mTintColorStateList;
+
+ @Before
+ public void setUp() {
+ mTileColorPicker = TileColorPicker.getInstance(mContext);
+ mTintColorStateList = mContext.getResources().
+ getColorStateList(R.color.tint_color_selector, mContext.getTheme());
+ }
+
+ @Test
+ public void testGetColor_StateUnavailable_ReturnUnavailableColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_UNAVAILABLE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.DISABLE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+
+ @Test
+ public void testGetColor_StateInactive_ReturnInactiveColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_INACTIVE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.INACTIVE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+
+ @Test
+ public void testGetColor_StateActive_ReturnActiveColor() {
+ final int color = mTileColorPicker.getColor(Tile.STATE_ACTIVE);
+ final int expectedColor = mTintColorStateList.getColorForState(
+ TileColorPicker.ENABLE_STATE_SET, DEFAULT_COLOR);
+
+ assertEquals(expectedColor, color);
+ }
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index b8a2340..3419963 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -826,25 +826,21 @@
+ " not in uid " + callingUid);
}
- // Legacy apps in permission review mode trigger a user prompt
- if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
- Intent intent = new Intent(intentAction);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- try {
- mContext.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- // Shouldn't happen
- Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
- return false;
- }
- return true;
+ Intent intent = new Intent(intentAction);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ // Shouldn't happen
+ Slog.e(TAG, "Intent to handle action " + intentAction + " missing");
+ return false;
}
+ return true;
} catch (PackageManager.NameNotFoundException e) {
throw new RemoteException(e.getMessage());
}
- return false;
}
public void unbindAndFinish() {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index eb2cd0b..d42fe11 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -154,7 +154,8 @@
3110 unknown_sources_enabled (value|1)
# Package Manager critical info
3120 pm_critical_info (msg|3)
-
+# Disk usage stats for verifying quota correctness
+3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
# ---------------------------
# WindowManagerService.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 87b4221..6625846 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -215,6 +215,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.BootTimingsTraceLog;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -3629,13 +3630,14 @@
* resources like shared libraries and access user-wide resources
*/
if (ArrayUtils.isEmpty(permGids)) {
- gids = new int[2];
+ gids = new int[3];
} else {
- gids = new int[permGids.length + 2];
- System.arraycopy(permGids, 0, gids, 2, permGids.length);
+ gids = new int[permGids.length + 3];
+ System.arraycopy(permGids, 0, gids, 3, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
- gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
+ gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
+ gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
checkTime(startTime, "startProcess: building args");
if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
@@ -12056,6 +12058,12 @@
}
}
+ /**
+ * @deprecated This method is only used by a few internal components and it will soon be
+ * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+ * No new code should be calling it.
+ */
+ @Deprecated
public void requestBugReport(int bugreportType) {
String extraOptions = null;
switch (bugreportType) {
@@ -13205,7 +13213,8 @@
}
}
- public void systemReady(final Runnable goingCallback) {
+ public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
+ traceLog.traceBegin("PhaseActivityManagerReady");
synchronized(this) {
if (mSystemReady) {
// If we're done calling all the receivers, run the next "boot phase" passed in
@@ -13302,7 +13311,7 @@
}
if (goingCallback != null) goingCallback.run();
-
+ traceLog.traceBegin("ActivityManagerStartApps");
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(currentUserId), currentUserId);
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
@@ -13377,6 +13386,8 @@
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
+ traceLog.traceEnd(); // ActivityManagerStartApps
+ traceLog.traceEnd(); // PhaseActivityManagerReady
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 788a28c..9ef793d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -232,6 +232,7 @@
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101;
private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
+ private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
// end of messages handled under wakelock
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -3183,7 +3184,7 @@
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_A2DP_SINK_CONNECTION_STATE,
state,
- 0,
+ 0 /* arg2 unused */,
btDevice,
delay);
}
@@ -3200,7 +3201,7 @@
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_A2DP_SRC_CONNECTION_STATE,
state,
- 0,
+ 0 /* arg2 unused */,
btDevice,
0 /* delay */);
}
@@ -3848,8 +3849,8 @@
int delay = checkSendBecomingNoisyIntent(type, state);
queueMsgUnderWakeLock(mAudioHandler,
MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
- 0,
- 0,
+ 0 /* arg1 unused */,
+ 0 /* arg2 unused */,
new WiredDeviceConnectionState(type, state, address, name, caller),
delay);
}
@@ -3872,13 +3873,25 @@
(profile == BluetoothProfile.A2DP ?
MSG_SET_A2DP_SINK_CONNECTION_STATE : MSG_SET_A2DP_SRC_CONNECTION_STATE),
state,
- 0,
+ 0 /* arg2 unused */,
device,
delay);
}
return delay;
}
+ public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device)
+ {
+ synchronized (mConnectedDevices) {
+ queueMsgUnderWakeLock(mAudioHandler,
+ MSG_A2DP_DEVICE_CONFIG_CHANGE,
+ 0 /* arg1 unused */,
+ 0 /* arg1 unused */,
+ device,
+ 0 /* delay */);
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////
@@ -4691,6 +4704,11 @@
mAudioEventWakeLock.release();
break;
+ case MSG_A2DP_DEVICE_CONFIG_CHANGE:
+ onBluetoothA2dpDeviceConfigChange((BluetoothDevice)msg.obj);
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_REPORT_NEW_ROUTES: {
int N = mRoutesObservers.beginBroadcast();
if (N > 0) {
@@ -4913,7 +4931,7 @@
private void onSetA2dpSinkConnectionState(BluetoothDevice btDevice, int state)
{
if (DEBUG_VOL) {
- Log.d(TAG, "onSetA2dpSinkConnectionState btDevice="+btDevice+"state="+state);
+ Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice+"state=" + state);
}
if (btDevice == null) {
return;
@@ -4924,9 +4942,9 @@
}
synchronized (mConnectedDevices) {
- String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- btDevice.getAddress());
- DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+ final String key = makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ btDevice.getAddress());
+ final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
boolean isConnected = deviceSpec != null;
if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -4977,7 +4995,7 @@
private void onSetA2dpSourceConnectionState(BluetoothDevice btDevice, int state)
{
if (DEBUG_VOL) {
- Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state);
+ Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" + state);
}
if (btDevice == null) {
return;
@@ -4988,8 +5006,8 @@
}
synchronized (mConnectedDevices) {
- String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
- DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+ final String key = makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
+ final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
boolean isConnected = deviceSpec != null;
if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
@@ -5000,6 +5018,31 @@
}
}
+ private void onBluetoothA2dpDeviceConfigChange(BluetoothDevice btDevice)
+ {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
+ }
+ if (btDevice == null) {
+ return;
+ }
+ String address = btDevice.getAddress();
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ address = "";
+ }
+
+ int device = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+ synchronized (mConnectedDevices) {
+ final String key = makeDeviceListKey(device, address);
+ final DeviceListSpec deviceSpec = mConnectedDevices.get(key);
+ if (deviceSpec != null) {
+ // Device is connected
+ AudioSystem.handleDeviceConfigChange(device, address,
+ btDevice.getName());
+ }
+ }
+ }
+
public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
synchronized (mA2dpAvrcpLock) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 4526ab74..7c5550a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -250,7 +250,8 @@
@ServiceThreadOnly
boolean dispatchMessage(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (mService.isPowerStandby() && mStandbyHandler.handleCommand(message)) {
+ if (mService.isPowerStandby() && !mService.isWakeUpMessageReceived()
+ && mStandbyHandler.handleCommand(message)) {
return true;
}
return super.onMessage(message);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 18f1b6c..4c6b832 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -967,6 +967,9 @@
}
void setAudioStatus(boolean mute, int volume) {
+ if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated()) {
+ return;
+ }
AudioManager audioManager = getAudioManager();
boolean muted = audioManager.isStreamMute(AudioManager.STREAM_MUSIC);
if (mute) {
@@ -2014,6 +2017,10 @@
// the intent, the sequence will continue at onStandby().
}
+ boolean isWakeUpMessageReceived() {
+ return mWakeUpMessageReceived;
+ }
+
@ServiceThreadOnly
private void onWakeUp() {
assertRunOnServiceThread();
@@ -2116,7 +2123,6 @@
device.onStandby(mStandbyMessageReceived, standbyAction);
}
mStandbyMessageReceived = false;
- mAddressAllocated = false;
mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 039fc50..aaba00a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -270,6 +270,7 @@
new ArrayList<NotificationRecord>();
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>();
+ final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
@@ -1614,35 +1615,60 @@
int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), incomingUserId, true, false,
"getAppActiveNotifications", pkg);
-
- final ArrayList<StatusBarNotification> list
- = new ArrayList<StatusBarNotification>(mNotificationList.size());
+ final ArrayMap<String, StatusBarNotification> map
+ = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
- final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
- && (sbn.getNotification().flags
- & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
- // We could pass back a cloneLight() but clients might get confused and
- // try to send this thing back to notify() again, which would not work
- // very well.
- final StatusBarNotification sbnOut = new StatusBarNotification(
- sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getNotificationChannel(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- sbn.getNotification().clone(),
- sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
- list.add(sbnOut);
+ StatusBarNotification sbn = sanitizeSbn(pkg, userId,
+ mNotificationList.get(i).sbn);
+ if (sbn != null) {
+ map.put(sbn.getKey(), sbn);
+ }
+ }
+ for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
+ StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
+ if (sbn != null) {
+ map.put(sbn.getKey(), sbn);
+ }
+ }
+ }
+ synchronized (mEnqueuedNotifications) {
+ final int N = mEnqueuedNotifications.size();
+ for (int i = 0; i < N; i++) {
+ StatusBarNotification sbn = sanitizeSbn(pkg, userId,
+ mEnqueuedNotifications.get(i).sbn);
+ if (sbn != null) {
+ map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
}
}
}
+ final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
+ list.addAll(map.values());
return new ParceledListSlice<StatusBarNotification>(list);
}
+ private StatusBarNotification sanitizeSbn(String pkg, int userId,
+ StatusBarNotification sbn) {
+ if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
+ && (sbn.getNotification().flags
+ & Notification.FLAG_AUTOGROUP_SUMMARY) == 0) {
+ // We could pass back a cloneLight() but clients might get confused and
+ // try to send this thing back to notify() again, which would not work
+ // very well.
+ return new StatusBarNotification(
+ sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getNotificationChannel(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ sbn.getNotification().clone(),
+ sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
+ }
+ return null;
+ }
+
/**
* System-only API for getting a list of recent (cleared, no longer shown) notifications.
*
@@ -2835,6 +2861,9 @@
// setup local book-keeping
final NotificationRecord r = new NotificationRecord(getContext(), n);
+ synchronized (mEnqueuedNotifications) {
+ mEnqueuedNotifications.add(r);
+ }
mHandler.post(new EnqueueNotificationRunnable(userId, r));
idOut[0] = id;
@@ -2851,121 +2880,126 @@
@Override
public void run() {
-
- synchronized (mNotificationList) {
- if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
- // TODO: log to event log
- if (DBG) {
- Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
- }
- mSnoozeHelper.update(userId, r);
- savePolicyFile();
- return;
- }
-
- final StatusBarNotification n = r.sbn;
- if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
- NotificationRecord old = mNotificationsByKey.get(n.getKey());
- if (old != null) {
- // Retain ranking information from previous record
- r.copyRankingInformation(old);
- }
-
- final int callingUid = n.getUid();
- final int callingPid = n.getInitialPid();
- final Notification notification = n.getNotification();
- final String pkg = n.getPackageName();
- final int id = n.getId();
- final String tag = n.getTag();
- final boolean isSystemNotification = isUidSystem(callingUid) ||
- ("android".equals(pkg));
-
- // Handle grouped notifications and bail out early if we
- // can to avoid extracting signals.
- handleGroupedNotificationLocked(r, old, callingUid, callingPid);
-
- // This conditional is a dirty hack to limit the logging done on
- // behalf of the download manager without affecting other apps.
- if (!pkg.equals("com.android.providers.downloads")
- || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
- int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
- if (old != null) {
- enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
- }
- EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
- pkg, id, tag, userId, notification.toString(),
- enqueueStatus);
- }
-
- mRankingHelper.extractSignals(r);
-
- // blocked apps
- if (isBlocked(r, mUsageStats)) {
- return;
- }
-
- // tell the assistant service about the notification
- if (mNotificationAssistants.isEnabled()) {
- mNotificationAssistants.onNotificationEnqueued(r);
- // TODO delay the code below here for 100ms or until there is an answer
- }
-
-
- int index = indexOfNotificationLocked(n.getKey());
- if (index < 0) {
- mNotificationList.add(r);
- mUsageStats.registerPostedByApp(r);
- } else {
- old = mNotificationList.get(index);
- mNotificationList.set(index, r);
- mUsageStats.registerUpdatedByApp(r, old);
- // Make sure we don't lose the foreground service state.
- notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
- r.isUpdate = true;
- }
-
- mNotificationsByKey.put(n.getKey(), r);
-
- // Ensure if this is a foreground service that the proper additional
- // flags are set.
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_NO_CLEAR;
- }
-
- applyZenModeLocked(r);
- mRankingHelper.sort(mNotificationList);
-
- if (notification.getSmallIcon() != null) {
- StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
- mListeners.notifyPostedLocked(n, oldSbn);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGroupHelper.onNotificationPosted(n);
+ try {
+ synchronized (mNotificationList) {
+ if (mSnoozeHelper.isSnoozed(userId, r.sbn.getPackageName(), r.getKey())) {
+ // TODO: log to event log
+ if (DBG) {
+ Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
}
- });
- } else {
- Slog.e(TAG, "Not posting notification without small icon: " + notification);
- if (old != null && !old.isCanceled) {
- mListeners.notifyRemovedLocked(n,
- NotificationListenerService.REASON_DELEGATE_ERROR);
+ mSnoozeHelper.update(userId, r);
+ savePolicyFile();
+ return;
+ }
+
+ final StatusBarNotification n = r.sbn;
+ if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
+ NotificationRecord old = mNotificationsByKey.get(n.getKey());
+ if (old != null) {
+ // Retain ranking information from previous record
+ r.copyRankingInformation(old);
+ }
+
+ final int callingUid = n.getUid();
+ final int callingPid = n.getInitialPid();
+ final Notification notification = n.getNotification();
+ final String pkg = n.getPackageName();
+ final int id = n.getId();
+ final String tag = n.getTag();
+ final boolean isSystemNotification = isUidSystem(callingUid) ||
+ ("android".equals(pkg));
+
+ // Handle grouped notifications and bail out early if we
+ // can to avoid extracting signals.
+ handleGroupedNotificationLocked(r, old, callingUid, callingPid);
+
+ // This conditional is a dirty hack to limit the logging done on
+ // behalf of the download manager without affecting other apps.
+ if (!pkg.equals("com.android.providers.downloads")
+ || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
+ int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
+ if (old != null) {
+ enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
+ }
+ EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
+ pkg, id, tag, userId, notification.toString(),
+ enqueueStatus);
+ }
+
+ mRankingHelper.extractSignals(r);
+
+ // blocked apps
+ if (isBlocked(r, mUsageStats)) {
+ return;
+ }
+
+ // tell the assistant service about the notification
+ if (mNotificationAssistants.isEnabled()) {
+ mNotificationAssistants.onNotificationEnqueued(r);
+ // TODO delay the code below here for 100ms or until there is an answer
+ }
+
+
+ int index = indexOfNotificationLocked(n.getKey());
+ if (index < 0) {
+ mNotificationList.add(r);
+ mUsageStats.registerPostedByApp(r);
+ } else {
+ old = mNotificationList.get(index);
+ mNotificationList.set(index, r);
+ mUsageStats.registerUpdatedByApp(r, old);
+ // Make sure we don't lose the foreground service state.
+ notification.flags |=
+ old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ r.isUpdate = true;
+ }
+
+ mNotificationsByKey.put(n.getKey(), r);
+
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
+ }
+
+ applyZenModeLocked(r);
+ mRankingHelper.sort(mNotificationList);
+
+ if (notification.getSmallIcon() != null) {
+ StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
+ mListeners.notifyPostedLocked(n, oldSbn);
mHandler.post(new Runnable() {
@Override
public void run() {
- mGroupHelper.onNotificationRemoved(n);
+ mGroupHelper.onNotificationPosted(n);
}
});
+ } else {
+ Slog.e(TAG, "Not posting notification without small icon: " + notification);
+ if (old != null && !old.isCanceled) {
+ mListeners.notifyRemovedLocked(n,
+ NotificationListenerService.REASON_DELEGATE_ERROR);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mGroupHelper.onNotificationRemoved(n);
+ }
+ });
+ }
+ // ATTENTION: in a future release we will bail out here
+ // so that we do not play sounds, show lights, etc. for invalid
+ // notifications
+ Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+ + n.getPackageName());
}
- // ATTENTION: in a future release we will bail out here
- // so that we do not play sounds, show lights, etc. for invalid
- // notifications
- Slog.e(TAG, "WARNING: In a future release this will crash the app: "
- + n.getPackageName());
- }
- buzzBeepBlinkLocked(r);
+ buzzBeepBlinkLocked(r);
+ }
+ } finally {
+ synchronized (mEnqueuedNotifications) {
+ mEnqueuedNotifications.remove(r);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 409eef4..733ff67 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -36,6 +36,9 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@@ -84,6 +87,14 @@
&& mSnoozedNotifications.get(userId).get(pkg).containsKey(key);
}
+ protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
+ if (mSnoozedNotifications.containsKey(userId)
+ && mSnoozedNotifications.get(userId).containsKey(pkg)) {
+ mSnoozedNotifications.get(userId).get(pkg).values();
+ }
+ return Collections.EMPTY_LIST;
+ }
+
/**
* Snoozes a notification and schedules an alarm to repost at that time.
*/
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 1f83d9e..605fa5d 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -54,6 +54,7 @@
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
public static final int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+ public static final int FLAG_USE_QUOTA = 1 << 12;
private final boolean mIsolated;
@@ -198,12 +199,13 @@
}
}
- public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
- String codePath, PackageStats stats) throws InstallerException {
+ public void getAppSize(String uuid, String packageName, int userId, int flags, int appId,
+ long ceDataInode, String codePath, String externalUuid, PackageStats stats)
+ throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
- codePath);
+ final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, appId,
+ ceDataInode, codePath, externalUuid);
stats.codeSize += res[0];
stats.dataSize += res[1];
stats.cacheSize += res[2];
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c85e1d8..d1aed3e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -703,8 +703,11 @@
for (File addedFile : addedFiles) {
final ApkLite apk;
try {
- apk = PackageParser.parseApkLite(
- addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;
+ if ((params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
+ flags |= PackageParser.PARSE_IS_EPHEMERAL;
+ }
+ apk = PackageParser.parseApkLite(addedFile, flags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 88c5a17..2dd9503 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -381,6 +381,9 @@
private static final boolean DISABLE_EPHEMERAL_APPS = false;
private static final boolean HIDE_EPHEMERAL_APIS = true;
+ private static final boolean ENABLE_QUOTA =
+ SystemProperties.getBoolean("persist.fw.quota", false);
+
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
@@ -17392,6 +17395,11 @@
mHandler.sendMessage(msg);
}
+ private boolean equals(PackageStats a, PackageStats b) {
+ return (a.codeSize == b.codeSize) && (a.dataSize == b.dataSize)
+ && (a.cacheSize == b.cacheSize);
+ }
+
private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
final PackageSetting ps;
synchronized (mPackages) {
@@ -17401,20 +17409,51 @@
return false;
}
}
+
+ final long ceDataInode = ps.getCeDataInode(userId);
+ final PackageStats quotaStats = new PackageStats(stats.packageName, stats.userHandle);
+
+ final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final String externalUuid = storage.getPrimaryStorageUuid();
try {
- mInstaller.getAppSize(ps.volumeUuid, packageName, userId,
- StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE,
- ps.getCeDataInode(userId), ps.codePathString, stats);
+ final long start = SystemClock.elapsedRealtimeNanos();
+ mInstaller.getAppSize(ps.volumeUuid, packageName, userId, 0,
+ ps.appId, ceDataInode, ps.codePathString, externalUuid, stats);
+ final long stopManual = SystemClock.elapsedRealtimeNanos();
+ if (ENABLE_QUOTA) {
+ mInstaller.getAppSize(ps.volumeUuid, packageName, userId, Installer.FLAG_USE_QUOTA,
+ ps.appId, ceDataInode, ps.codePathString, externalUuid, quotaStats);
+ }
+ final long stopQuota = SystemClock.elapsedRealtimeNanos();
+
+ // For now, ignore code size of packages on system partition
+ if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
+ stats.codeSize = 0;
+ quotaStats.codeSize = 0;
+ }
+
+ if (ENABLE_QUOTA && Build.IS_ENG && !ps.isSharedUser()) {
+ if (!equals(stats, quotaStats)) {
+ Log.w(TAG, "Found discrepancy between statistics:");
+ Log.w(TAG, "Manual: " + stats);
+ Log.w(TAG, "Quota: " + quotaStats);
+ }
+ final long manualTime = stopManual - start;
+ final long quotaTime = stopQuota - stopManual;
+ EventLogTags.writePmPackageStats(manualTime, quotaTime,
+ stats.dataSize, quotaStats.dataSize,
+ stats.cacheSize, quotaStats.cacheSize);
+ }
+
+ // External clients expect these to be tracked separately
+ stats.dataSize -= stats.cacheSize;
+ quotaStats.dataSize -= quotaStats.cacheSize;
+
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
return false;
}
- // For now, ignore code size of packages on system partition
- if (isSystemApp(ps) && !isUpdatedSystemApp(ps)) {
- stats.codeSize = 0;
- }
-
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9737b36..22f9f3a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -544,8 +544,10 @@
if (Intent.ACTION_USER_UNLOCKED.equals(action)
|| Intent.ACTION_USER_STARTED.equals(action)
|| KeyChain.ACTION_TRUST_STORE_CHANGED.equals(action)) {
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
- new MonitoringCertNotificationTask().execute(userId);
+ if (!StorageManager.inCryptKeeperBounce()) {
+ new MonitoringCertNotificationTask().execute(
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL));
+ }
}
if (Intent.ACTION_USER_ADDED.equals(action)) {
sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
@@ -5978,8 +5980,9 @@
throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
+ final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userId, admin);
synchronized (this) {
- enforceCanSetDeviceOwnerLocked(admin, userId);
+ enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccounts);
final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
if (activeAdmin == null
|| getUserData(userId).mRemovingAdmins.contains(admin)) {
@@ -6215,8 +6218,9 @@
throw new IllegalArgumentException("Component " + who
+ " not installed for userId:" + userHandle);
}
+ final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userHandle, who);
synchronized (this) {
- enforceCanSetProfileOwnerLocked(who, userHandle);
+ enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccounts);
if (getActiveAdminUncheckedLocked(who, userHandle) == null
|| getUserData(userHandle).mRemovingAdmins.contains(who)) {
@@ -6549,9 +6553,10 @@
* The profile owner can only be set before the user setup phase has completed,
* except for:
* - SYSTEM_UID
- * - adb if there are no accounts. (But see {@link #hasIncompatibleAccountsLocked})
+ * - adb unless hasIncompatibleAccounts is true.
*/
- private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle) {
+ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+ boolean hasIncompatibleAccounts) {
UserInfo info = getUserInfo(userHandle);
if (info == null) {
// User doesn't exist.
@@ -6571,7 +6576,7 @@
}
if (isAdb()) {
if ((mIsWatch || hasUserSetupCompleted(userHandle))
- && hasIncompatibleAccountsLocked(userHandle, owner)) {
+ && hasIncompatibleAccounts) {
throw new IllegalStateException("Not allowed to set the profile owner because "
+ "there are already some accounts on the profile");
}
@@ -6588,12 +6593,14 @@
* The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
* permission.
*/
- private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId) {
+ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId,
+ boolean hasIncompatibleAccounts) {
if (!isAdb()) {
enforceCanManageProfileAndDeviceOwners();
}
- final int code = checkDeviceOwnerProvisioningPreConditionLocked(owner, userId, isAdb());
+ final int code = checkDeviceOwnerProvisioningPreConditionLocked(
+ owner, userId, isAdb(), hasIncompatibleAccounts);
switch (code) {
case CODE_OK:
return;
@@ -8902,7 +8909,7 @@
* except for adb command if no accounts or additional users are present on the device.
*/
private int checkDeviceOwnerProvisioningPreConditionLocked(@Nullable ComponentName owner,
- int deviceOwnerUserId, boolean isAdb) {
+ int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccounts) {
if (mOwners.hasDeviceOwner()) {
return CODE_HAS_DEVICE_OWNER;
}
@@ -8922,7 +8929,7 @@
if (mUserManager.getUserCount() > 1) {
return CODE_NONSYSTEM_USER_EXISTS;
}
- if (hasIncompatibleAccountsLocked(UserHandle.USER_SYSTEM, owner)) {
+ if (hasIncompatibleAccounts) {
return CODE_ACCOUNTS_NOT_EMPTY;
}
} else {
@@ -8949,8 +8956,9 @@
private int checkDeviceOwnerProvisioningPreCondition(int deviceOwnerUserId) {
synchronized (this) {
+ // hasIncompatibleAccounts doesn't matter since the caller is not adb.
return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
- deviceOwnerUserId, /* isAdb= */ false);
+ deviceOwnerUserId, /* isAdb= */ false, /* hasIncompatibleAccounts=*/ true);
}
}
@@ -9848,8 +9856,15 @@
* - Otherwise, if there's any account that does not have ..._ALLOWED, or does have
* ..._DISALLOWED, return true.
* - Otherwise return false.
+ *
+ * DO NOT CALL IT WITH THE DPMS LOCK HELD.
*/
- private boolean hasIncompatibleAccountsLocked(int userId, @Nullable ComponentName owner) {
+ private boolean hasIncompatibleAccountsNoLock(int userId, @Nullable ComponentName owner) {
+ if (Thread.holdsLock(this)) {
+ Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held.");
+ return true;
+ }
+
final long token = mInjector.binderClearCallingIdentity();
try {
final AccountManager am = AccountManager.get(mContext);
@@ -9857,22 +9872,30 @@
if (accounts.length == 0) {
return false;
}
+ synchronized (this) {
+ if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
+ Log.w(LOG_TAG,
+ "Non test-only owner can't be installed with existing accounts.");
+ return true;
+ }
+ }
+
final String[] feature_allow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED };
final String[] feature_disallow =
{ DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED };
- // Even if we find incompatible accounts along the way, we still check all accounts
- // for logging.
boolean compatible = true;
for (Account account : accounts) {
if (hasAccountFeatures(am, account, feature_disallow)) {
Log.e(LOG_TAG, account + " has " + feature_disallow[0]);
compatible = false;
+ break;
}
if (!hasAccountFeatures(am, account, feature_allow)) {
Log.e(LOG_TAG, account + " doesn't have " + feature_allow[0]);
compatible = false;
+ break;
}
}
if (compatible) {
@@ -9880,28 +9903,6 @@
} else {
Log.e(LOG_TAG, "Found incompatible accounts");
}
-
- // Then check if the owner is test-only.
- String log;
- if (owner == null) {
- // Owner is unknown. Suppose it's not test-only
- compatible = false;
- log = "Only test-only device/profile owner can be installed with accounts";
- } else if (isAdminTestOnlyLocked(owner, userId)) {
- if (compatible) {
- log = "Installing test-only owner " + owner;
- } else {
- log = "Can't install test-only owner " + owner + " with incompatible accounts";
- }
- } else {
- compatible = false;
- log = "Can't install non test-only owner " + owner + " with accounts";
- }
- if (compatible) {
- Log.w(LOG_TAG, log);
- } else {
- Log.e(LOG_TAG, log);
- }
return !compatible;
} finally {
mInjector.binderRestoreCallingIdentity(token);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be13499..7ccae2c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -710,10 +710,12 @@
traceEnd();
}
+ traceBeginAndSlog("SetWindowManagerService");
mActivityManagerService.setWindowManager(wm);
+ traceEnd();
- inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
traceBeginAndSlog("StartInputManager");
+ inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
traceEnd();
@@ -1238,8 +1240,10 @@
}
if (!disableNetwork && !disableNonCoreServices && EmergencyAffordanceManager.ENABLED) {
- // EmergencyMode sevice
+ // EmergencyMode service
+ traceBeginAndSlog("StartEmergencyAffordanceService");
mSystemServiceManager.startService(EmergencyAffordanceService.class);
+ traceEnd();
}
if (!disableNonCoreServices) {
@@ -1482,176 +1486,167 @@
// where third party code can really run (but before it has actually
// started launching the initial applications), for us to complete our
// initialization.
- mActivityManagerService.systemReady(new Runnable() {
- @Override
- public void run() {
- Slog.i(TAG, "Making services ready");
- traceBeginAndSlog("StartActivityManagerReadyPhase");
- mSystemServiceManager.startBootPhase(
- SystemService.PHASE_ACTIVITY_MANAGER_READY);
- traceEnd();
- traceBeginAndSlog("PhaseActivityManagerReady");
-
- traceBeginAndSlog("StartObservingNativeCrashes");
- try {
- mActivityManagerService.startObservingNativeCrashes();
- } catch (Throwable e) {
- reportWtf("observing native crashes", e);
- }
- traceEnd();
-
- if (!mOnlyCore) {
- Slog.i(TAG, "WebViewFactory preparation");
- traceBeginAndSlog("WebViewFactoryPreparation");
- mWebViewUpdateService.prepareWebViewInSystemServer();
- traceEnd();
- }
-
- traceBeginAndSlog("StartSystemUI");
- try {
- startSystemUi(context);
- } catch (Throwable e) {
- reportWtf("starting System UI", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeNetworkScoreReady");
- try {
- if (networkScoreF != null) networkScoreF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Network Score Service ready", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeNetworkManagementServiceReady");
- try {
- if (networkManagementF != null) networkManagementF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Network Managment Service ready", e);
- }
- CountDownLatch networkPolicyInitReadySignal = null;
- if (networkPolicyF != null) {
- networkPolicyInitReadySignal = networkPolicyF
- .networkScoreAndNetworkManagementServiceReady();
- }
- traceEnd();
- traceBeginAndSlog("MakeNetworkStatsServiceReady");
- try {
- if (networkStatsF != null) networkStatsF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Network Stats Service ready", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeConnectivityServiceReady");
- try {
- if (connectivityF != null) connectivityF.systemReady();
- } catch (Throwable e) {
- reportWtf("making Connectivity Service ready", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeNetworkPolicyServiceReady");
- try {
- if (networkPolicyF != null) {
- networkPolicyF.systemReady(networkPolicyInitReadySignal);
- }
- } catch (Throwable e) {
- reportWtf("making Network Policy Service ready", e);
- }
- traceEnd();
-
- traceBeginAndSlog("StartWatchdog");
- Watchdog.getInstance().start();
- traceEnd();
-
- // It is now okay to let the various system services start their
- // third party code...
- traceBeginAndSlog("PhaseThirdPartyAppsCanStart");
- mSystemServiceManager.startBootPhase(
- SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
- traceEnd();
-
- traceBeginAndSlog("MakeLocationServiceReady");
- try {
- if (locationF != null) locationF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying Location Service running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeCountryDetectionServiceReady");
- try {
- if (countryDetectorF != null) countryDetectorF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying CountryDetectorService running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeNetworkTimeUpdateReady");
- try {
- if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying NetworkTimeService running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeCommonTimeManagementServiceReady");
- try {
- if (commonTimeMgmtServiceF != null) {
- commonTimeMgmtServiceF.systemRunning();
- }
- } catch (Throwable e) {
- reportWtf("Notifying CommonTimeManagementService running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeInputManagerServiceReady");
- try {
- // TODO(BT) Pass parameter to input manager
- if (inputManagerF != null) inputManagerF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying InputManagerService running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeTelephonyRegistryReady");
- try {
- if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying TelephonyRegistry running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeMediaRouterServiceReady");
- try {
- if (mediaRouterF != null) mediaRouterF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying MediaRouterService running", e);
- }
- traceEnd();
- traceBeginAndSlog("MakeMmsServiceReady");
- try {
- if (mmsServiceF != null) mmsServiceF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying MmsService running", e);
- }
- traceEnd();
-
- traceBeginAndSlog("MakeNetworkScoreServiceReady");
- try {
- if (networkScoreF != null) networkScoreF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying NetworkScoreService running", e);
- }
- traceEnd();
-
- traceBeginAndSlog("IncidentDaemonReady");
- try {
- // TODO: Switch from checkService to getService once it's always
- // in the build and should reliably be there.
- final IIncidentManager incident = IIncidentManager.Stub.asInterface(
- ServiceManager.checkService("incident"));
- if (incident != null) incident.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying incident daemon running", e);
- }
- traceEnd();
-
-
- traceEnd(); // PhaseActivityManagerReady
+ mActivityManagerService.systemReady(() -> {
+ Slog.i(TAG, "Making services ready");
+ traceBeginAndSlog("StartActivityManagerReadyPhase");
+ mSystemServiceManager.startBootPhase(
+ SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ traceEnd();
+ traceBeginAndSlog("StartObservingNativeCrashes");
+ try {
+ mActivityManagerService.startObservingNativeCrashes();
+ } catch (Throwable e) {
+ reportWtf("observing native crashes", e);
}
- });
+ traceEnd();
+
+ if (!mOnlyCore) {
+ Slog.i(TAG, "WebViewFactory preparation");
+ traceBeginAndSlog("WebViewFactoryPreparation");
+ mWebViewUpdateService.prepareWebViewInSystemServer();
+ traceEnd();
+ }
+
+ traceBeginAndSlog("StartSystemUI");
+ try {
+ startSystemUi(context);
+ } catch (Throwable e) {
+ reportWtf("starting System UI", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeNetworkScoreReady");
+ try {
+ if (networkScoreF != null) networkScoreF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Network Score Service ready", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeNetworkManagementServiceReady");
+ try {
+ if (networkManagementF != null) networkManagementF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Network Managment Service ready", e);
+ }
+ CountDownLatch networkPolicyInitReadySignal = null;
+ if (networkPolicyF != null) {
+ networkPolicyInitReadySignal = networkPolicyF
+ .networkScoreAndNetworkManagementServiceReady();
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeNetworkStatsServiceReady");
+ try {
+ if (networkStatsF != null) networkStatsF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Network Stats Service ready", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeConnectivityServiceReady");
+ try {
+ if (connectivityF != null) connectivityF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Connectivity Service ready", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeNetworkPolicyServiceReady");
+ try {
+ if (networkPolicyF != null) {
+ networkPolicyF.systemReady(networkPolicyInitReadySignal);
+ }
+ } catch (Throwable e) {
+ reportWtf("making Network Policy Service ready", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartWatchdog");
+ Watchdog.getInstance().start();
+ traceEnd();
+
+ // It is now okay to let the various system services start their
+ // third party code...
+ traceBeginAndSlog("PhaseThirdPartyAppsCanStart");
+ mSystemServiceManager.startBootPhase(
+ SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+ traceEnd();
+
+ traceBeginAndSlog("MakeLocationServiceReady");
+ try {
+ if (locationF != null) locationF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying Location Service running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeCountryDetectionServiceReady");
+ try {
+ if (countryDetectorF != null) countryDetectorF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying CountryDetectorService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeNetworkTimeUpdateReady");
+ try {
+ if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying NetworkTimeService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeCommonTimeManagementServiceReady");
+ try {
+ if (commonTimeMgmtServiceF != null) {
+ commonTimeMgmtServiceF.systemRunning();
+ }
+ } catch (Throwable e) {
+ reportWtf("Notifying CommonTimeManagementService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeInputManagerServiceReady");
+ try {
+ // TODO(BT) Pass parameter to input manager
+ if (inputManagerF != null) inputManagerF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying InputManagerService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeTelephonyRegistryReady");
+ try {
+ if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying TelephonyRegistry running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeMediaRouterServiceReady");
+ try {
+ if (mediaRouterF != null) mediaRouterF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying MediaRouterService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("MakeMmsServiceReady");
+ try {
+ if (mmsServiceF != null) mmsServiceF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying MmsService running", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("MakeNetworkScoreServiceReady");
+ try {
+ if (networkScoreF != null) networkScoreF.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying NetworkScoreService running", e);
+ }
+ traceEnd();
+ traceBeginAndSlog("IncidentDaemonReady");
+ try {
+ // TODO: Switch from checkService to getService once it's always
+ // in the build and should reliably be there.
+ final IIncidentManager incident = IIncidentManager.Stub.asInterface(
+ ServiceManager.checkService("incident"));
+ if (incident != null) incident.systemRunning();
+ } catch (Throwable e) {
+ reportWtf("Notifying incident daemon running", e);
+ }
+ traceEnd();
+ }, BOOT_TIMINGS_TRACE_LOG);
}
static final void startSystemUi(Context context) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 69d27f2..9a9f81e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -53,6 +53,7 @@
import android.net.INetworkRecommendationProvider;
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppManager;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.RecommendationRequest;
@@ -134,9 +135,10 @@
mNetworkScoreService.systemRunning();
- verify(mContext).bindServiceAsUser(MockUtils.checkIntent(new Intent().setComponent(
- new ComponentName(NEW_SCORER.packageName,
- NEW_SCORER.recommendationServiceClassName))),
+ verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
+ new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
+ .setComponent(new ComponentName(NEW_SCORER.packageName,
+ NEW_SCORER.recommendationServiceClassName))),
any(ServiceConnection.class),
eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
eq(UserHandle.SYSTEM));
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 1247b2d..65255d9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -16,6 +16,8 @@
package com.android.server.devicepolicy;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
@@ -273,6 +275,7 @@
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
public final TelephonyManager telephonyManager;
+ public final AccountManager accountManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -315,6 +318,7 @@
wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
telephonyManager = mock(TelephonyManager.class);
+ accountManager = mock(AccountManager.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(context.getPackageManager());
@@ -375,6 +379,7 @@
}
}
);
+ when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
// Create a data directory.
final File dir = new File(dataDir, "user" + userId);
@@ -464,6 +469,8 @@
return powerManager;
case Context.WIFI_SERVICE:
return wifiManager;
+ case Context.ACCOUNT_SERVICE:
+ return accountManager;
}
throw new UnsupportedOperationException();
}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 989f72c..10cb7c9 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -239,6 +239,13 @@
*/
public static final int DIALED_CALL_FORWARDING_WHILE_ROAMING = 57;
+ /**
+ * The network does not accept the emergency call request because IMEI was used as
+ * identification and this cability is not supported by the network.
+ * {@hide}
+ */
+ public static final int IMEI_NOT_ACCEPTED = 58;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Please assign the new type the next id value below.
@@ -247,14 +254,14 @@
// 4) Update toString() with the newly added disconnect type.
// 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause.
//
- // NextId: 58
+ // NextId: 59
//*********************************************************************************************
/** Smallest valid value for call disconnect codes. */
public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
/** Largest valid value for call disconnect codes. */
- public static final int MAXIMUM_VALID_VALUE = DIALED_CALL_FORWARDING_WHILE_ROAMING;
+ public static final int MAXIMUM_VALID_VALUE = IMEI_NOT_ACCEPTED;
/** Private constructor to avoid class instantiation. */
private DisconnectCause() {
@@ -378,6 +385,8 @@
return "DIALED_ON_WRONG_SLOT";
case DIALED_CALL_FORWARDING_WHILE_ROAMING:
return "DIALED_CALL_FORWARDING_WHILE_ROAMING";
+ case IMEI_NOT_ACCEPTED:
+ return "IMEI_NOT_ACCEPTED";
default:
return "INVALID: " + cause;
}