Merge "Add test UsageStats App" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 37008e4..f0ee69d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2746,6 +2746,7 @@
method public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(java.lang.String, java.lang.String, java.lang.String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
method public android.accounts.AuthenticatorDescription[] getAuthenticatorTypes();
method public java.lang.String getPassword(android.accounts.Account);
+ method public java.lang.String getPreviousName(android.accounts.Account);
method public java.lang.String getUserData(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, java.lang.String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void invalidateAuthToken(java.lang.String, java.lang.String);
@@ -2753,6 +2754,7 @@
method public java.lang.String peekAuthToken(android.accounts.Account, java.lang.String);
method public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler);
method public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener);
+ method public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, java.lang.String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler);
method public void setAuthToken(android.accounts.Account, java.lang.String, java.lang.String);
method public void setPassword(android.accounts.Account, java.lang.String);
method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String);
@@ -16164,10 +16166,11 @@
ctor public MediaBrowser(android.content.Context, android.content.ComponentName, android.media.browse.MediaBrowser.ConnectionCallback, android.os.Bundle);
method public void connect();
method public void disconnect();
+ method public android.os.Bundle getExtras();
method public android.net.Uri getRoot();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
- method public void loadThumbnail(android.net.Uri, int, int, int, android.media.browse.MediaBrowser.ThumbnailCallback);
+ method public void loadThumbnail(android.net.Uri, int, int, android.media.browse.MediaBrowser.ThumbnailCallback);
method public void subscribe(android.net.Uri, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(android.net.Uri);
}
@@ -16195,6 +16198,8 @@
method public int describeContents();
method public android.os.Bundle getExtras();
method public int getFlags();
+ method public int getIconResId();
+ method public android.net.Uri getIconUri();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public android.net.Uri getUri();
@@ -16210,6 +16215,8 @@
ctor public MediaBrowserItem.Builder(android.net.Uri, int, java.lang.CharSequence);
method public android.media.browse.MediaBrowserItem build();
method public android.media.browse.MediaBrowserItem.Builder setExtras(android.os.Bundle);
+ method public android.media.browse.MediaBrowserItem.Builder setIconResId(int);
+ method public android.media.browse.MediaBrowserItem.Builder setIconUri(android.net.Uri);
method public android.media.browse.MediaBrowserItem.Builder setSummary(java.lang.CharSequence);
}
@@ -16217,16 +16224,19 @@
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public android.media.session.MediaSession.Token getSessionToken();
- method public void notifyChange();
method public void notifyChildrenChanged(android.net.Uri);
method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.net.Uri onGetRoot(java.lang.String, int, android.os.Bundle);
- method public abstract android.graphics.Bitmap onGetThumbnail(android.net.Uri, int, int, int);
- method public abstract java.util.List<android.media.browse.MediaBrowserItem> onLoadChildren(android.net.Uri);
+ method protected abstract android.media.browse.MediaBrowserService.BrowserRoot onGetRoot(java.lang.String, int, android.os.Bundle);
+ method protected abstract android.graphics.Bitmap onGetThumbnail(android.net.Uri, int, int);
+ method protected abstract java.util.List<android.media.browse.MediaBrowserItem> onLoadChildren(android.net.Uri);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_ACTION = "android.media.browse.MediaBrowserService";
}
+ public static class MediaBrowserService.BrowserRoot {
+ ctor public MediaBrowserService.BrowserRoot(android.net.Uri, android.os.Bundle);
+ }
+
}
package android.media.effect {
@@ -27322,7 +27332,6 @@
package android.service.voice {
public class AlwaysOnHotwordDetector {
- method public int getAvailability();
method public android.content.Intent getManageIntent(int);
method public int getSupportedRecognitionModes();
method public int startRecognition(int);
@@ -27344,6 +27353,7 @@
}
public static abstract interface AlwaysOnHotwordDetector.Callback {
+ method public abstract void onAvailabilityChanged(int);
method public abstract void onDetected(byte[]);
method public abstract void onDetectionStopped();
}
@@ -27355,7 +27365,6 @@
method public android.os.IBinder onBind(android.content.Intent);
method public void onReady();
method public void onShutdown();
- method public void onSoundModelsChanged();
method public void startSession(android.os.Bundle);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
@@ -28706,14 +28715,13 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public class RemoteCallVideoClient implements android.os.IBinder.DeathRecipient {
- method public void binderDied();
- method public void handleCallSessionEvent(int) throws android.os.RemoteException;
- method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities) throws android.os.RemoteException;
- method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void updateCallDataUsage(int) throws android.os.RemoteException;
- method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
+ public class RemoteCallVideoClient {
+ method public void handleCallSessionEvent(int);
+ method public void handleCameraCapabilitiesChange(android.telecomm.CallCameraCapabilities);
+ method public void receiveSessionModifyRequest(android.telecomm.VideoCallProfile);
+ method public void receiveSessionModifyResponse(int, android.telecomm.VideoCallProfile, android.telecomm.VideoCallProfile);
+ method public void updateCallDataUsage(int);
+ method public void updatePeerDimensions(int, int);
}
public class RemoteCallVideoProvider {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index aab6e80..09b484b 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -663,6 +663,72 @@
}
/**
+ * Rename the specified {@link Account}. This is equivalent to removing
+ * the existing account and adding a new renamed account with the old
+ * account's user data.
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
+ * and have the same UID as the account's authenticator.
+ *
+ * @param account The {@link Account} to rename
+ * @param newName String name to be associated with the account.
+ * @param callback Callback to invoke when the request completes, null for
+ * no callback
+ * @param handler {@link Handler} identifying the callback thread, null for
+ * the main thread
+ * @return An {@link AccountManagerFuture} which resolves to the Account
+ * after the name change. If successful the account's name will be the
+ * specified new name.
+ */
+ public AccountManagerFuture<Account> renameAccount(
+ final Account account,
+ final String newName,
+ AccountManagerCallback<Account> callback,
+ Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null.");
+ if (TextUtils.isEmpty(newName)) {
+ throw new IllegalArgumentException("newName is empty or null.");
+ }
+ return new Future2Task<Account>(handler, callback) {
+ @Override
+ public void doWork() throws RemoteException {
+ mService.renameAccount(mResponse, account, newName);
+ }
+ @Override
+ public Account bundleToResult(Bundle bundle) throws AuthenticatorException {
+ String name = bundle.getString(KEY_ACCOUNT_NAME);
+ String type = bundle.getString(KEY_ACCOUNT_TYPE);
+ return new Account(name, type);
+ }
+ }.start();
+ }
+
+ /**
+ * Gets the previous name associated with the account or {@code null}, if
+ * none. This is intended so that clients of {@link
+ * #LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts can determine if an
+ * authenticator has renamed an account.
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * @param account The account to query for a previous name.
+ * @return The account's previous name, null if the account has never been
+ * renamed.
+ */
+ public String getPreviousName(final Account account) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ try {
+ return mService.getPreviousName(account);
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
* Removes an account from the AccountManager. Does nothing if the account
* does not exist. Does not delete the account from the server.
* The authenticator may have its own policies preventing account
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index a04875d..a133788 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -69,4 +69,10 @@
boolean addSharedAccountAsUser(in Account account, int userId);
Account[] getSharedAccountsAsUser(int userId);
boolean removeSharedAccountAsUser(in Account account, int userId);
+
+ /* Account renaming. */
+ void renameAccount(in IAccountManagerResponse response, in Account accountToRename, String newName);
+ String getPreviousName(in Account account);
+ boolean renameSharedAccountAsUser(in Account accountToRename, String newName, int userId);
+
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4d6bd4b..175b6a5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -413,8 +413,7 @@
registerService(CONNECTIVITY_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(CONNECTIVITY_SERVICE);
- return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b),
- ctx.getPackageName());
+ return new ConnectivityManager(IConnectivityManager.Stub.asInterface(b));
}});
registerService(COUNTRY_DETECTOR, new StaticServiceFetcher() {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 8de545e..13364cd 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -543,8 +543,6 @@
private final IConnectivityManager mService;
- private final String mPackageName;
-
private INetworkManagementService mNMService;
/**
@@ -646,23 +644,6 @@
}
/**
- * Checks if the given network type should be exempt from VPN routing rules
- *
- * @hide
- */
- public static boolean isNetworkTypeExempt(int networkType) {
- switch (networkType) {
- case TYPE_MOBILE_MMS:
- case TYPE_MOBILE_SUPL:
- case TYPE_MOBILE_HIPRI:
- case TYPE_MOBILE_IA:
- return true;
- default:
- return false;
- }
- }
-
- /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
*
@@ -1236,13 +1217,7 @@
* {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
- InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
-
- if (inetAddress == null) {
- return false;
- }
-
- return requestRouteToHostAddress(networkType, inetAddress);
+ return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
}
/**
@@ -1260,9 +1235,8 @@
* {@link #setProcessDefaultNetwork} api.
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
- byte[] address = hostAddress.getAddress();
try {
- return mService.requestRouteToHostAddress(networkType, address, mPackageName);
+ return mService.requestRouteToHostAddress(networkType, hostAddress.getAddress());
} catch (RemoteException e) {
return false;
}
@@ -1433,9 +1407,8 @@
/**
* {@hide}
*/
- public ConnectivityManager(IConnectivityManager service, String packageName) {
+ public ConnectivityManager(IConnectivityManager service) {
mService = checkNotNull(service, "missing IConnectivityManager");
- mPackageName = checkNotNull(packageName, "missing package name");
}
/** {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index f61984a..8b12fb8 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -69,9 +69,7 @@
int stopUsingNetworkFeature(int networkType, in String feature);
- boolean requestRouteToHost(int networkType, int hostAddress, String packageName);
-
- boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress, String packageName);
+ boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
/** Policy control over specific {@link NetworkStateTracker}. */
void setPolicyDataEnable(int networkType, boolean enabled);
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 9b95305..663aa15 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -36,12 +36,6 @@
private static final String TAG = "NetworkUtils";
- /** Bring the named network interface up. */
- public native static int enableInterface(String interfaceName);
-
- /** Bring the named network interface down. */
- public native static int disableInterface(String interfaceName);
-
/** Setting bit 0 indicates reseting of IPv4 addresses required */
public static final int RESET_IPV4_ADDRESSES = 0x01;
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 403f2c9..c1e6664 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -19,7 +19,6 @@
import android.net.InterfaceConfiguration;
import android.net.INetworkManagementEventObserver;
-import android.net.LinkAddress;
import android.net.NetworkStats;
import android.net.RouteInfo;
import android.net.UidRange;
@@ -313,11 +312,6 @@
*/
void setDnsServersForNetwork(int netId, in String[] servers, String domains);
- /**
- * Flush the DNS cache associated with the specified network.
- */
- void flushNetworkDnsCache(int netId);
-
void setFirewallEnabled(boolean enabled);
boolean isFirewallEnabled();
void setFirewallInterfaceRule(String iface, boolean allow);
@@ -336,17 +330,6 @@
void removeVpnUidRanges(int netId, in UidRange[] ranges);
/**
- * Exempts {@code host} from the routing set up by {@link setMarkedForwardingRoute}
- * All connects to {@code host} will use the global routing table
- */
- void setHostExemption(in LinkAddress host);
-
- /**
- * Clears an exemption set by {@link setHostExemption}
- */
- void clearHostExemption(in LinkAddress host);
-
- /**
* Start the clatd (464xlat) service
*/
void startClatd(String interfaceName);
@@ -402,7 +385,6 @@
void removeInterfaceFromNetwork(String iface, int netId);
void addLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
- void removeLegacyRouteForNetId(int netId, in RouteInfo routeInfo, int uid);
void setDefaultNetId(int netId);
void clearDefaultNetId();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 27a7b8e..d685cc5 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -27,6 +27,7 @@
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
@@ -41,7 +42,7 @@
* always-on keyphrase detection APIs.
*/
public class AlwaysOnHotwordDetector {
- //---- States of Keyphrase availability. Return codes for getAvailability() ----//
+ //---- States of Keyphrase availability. Return codes for onAvailabilityChanged() ----//
/**
* Indicates that this hotword detector is no longer valid for any recognition
* and should not be used anymore.
@@ -66,6 +67,11 @@
*/
public static final int STATE_KEYPHRASE_ENROLLED = 2;
+ /**
+ * Indicates that the detector isn't ready currently.
+ */
+ private static final int STATE_NOT_READY = 0;
+
// Keyphrase management actions. Used in getManageIntent() ----//
/** Indicates that we need to enroll. */
public static final int MANAGE_ACTION_ENROLL = 0;
@@ -104,9 +110,12 @@
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
static final String TAG = "AlwaysOnHotwordDetector";
+ // TODO: Set to false.
+ static final boolean DBG = true;
- private static final int MSG_HOTWORD_DETECTED = 1;
- private static final int MSG_DETECTION_STOPPED = 2;
+ private static final int MSG_STATE_CHANGED = 1;
+ private static final int MSG_HOTWORD_DETECTED = 2;
+ private static final int MSG_DETECTION_STOPPED = 3;
private final String mText;
private final String mLocale;
@@ -120,21 +129,40 @@
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
private final Callback mExternalCallback;
- private final boolean mDisabled;
private final Object mLock = new Object();
+ private final Handler mHandler;
/**
* The sound model for the keyphrase, derived from the model management service
* (IVoiceInteractionManagerService). May be null if the keyphrase isn't enrolled yet.
*/
private KeyphraseSoundModel mEnrolledSoundModel;
- private boolean mInvalidated;
+ private int mAvailability = STATE_NOT_READY;
/**
* Callbacks for always-on hotword detection.
*/
public interface Callback {
/**
+ * Called when the hotword availability changes.
+ * This indicates a change in the availability of recognition for the given keyphrase.
+ * It's called at least once with the initial availability.<p/>
+ *
+ * Availability implies whether the hardware on this system is capable of listening for
+ * the given keyphrase or not. <p/>
+ * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or
+ * {@link #STATE_KEYPHRASE_UNSUPPORTED},
+ * detection is not possible and no further interaction should be
+ * performed with this detector. <br/>
+ * If it is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin
+ * an enrollment flow for the keyphrase. <br/>
+ * and for {@link #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <p/>
+ *
+ * If the return code is {@link #STATE_INVALID}, this detector is stale.
+ * A new detector should be obtained for use in the future.
+ */
+ void onAvailabilityChanged(int status);
+ /**
* Called when the keyphrase is spoken.
*
* @param data Optional trigger audio data, if it was requested during
@@ -160,54 +188,24 @@
KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
IVoiceInteractionService voiceInteractionService,
IVoiceInteractionManagerService modelManagementService) {
- mInvalidated = false;
mText = text;
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
mExternalCallback = callback;
- mInternalCallback = new SoundTriggerListener(new MyHandler());
+ mHandler = new MyHandler();
+ mInternalCallback = new SoundTriggerListener(mHandler);
mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
- if (mKeyphraseMetadata != null) {
- mEnrolledSoundModel = internalGetKeyphraseSoundModelLocked(mKeyphraseMetadata.id);
- }
- int initialAvailability = internalGetAvailabilityLocked();
- mDisabled = (initialAvailability == STATE_HARDWARE_UNAVAILABLE)
- || (initialAvailability == STATE_KEYPHRASE_UNSUPPORTED);
- }
-
- /**
- * Gets the state of always-on hotword detection for the given keyphrase and locale
- * on this system.
- * Availability implies that the hardware on this system is capable of listening for
- * the given keyphrase or not. <p/>
- * If the return code is one of {@link #STATE_HARDWARE_UNAVAILABLE} or
- * {@link #STATE_KEYPHRASE_UNSUPPORTED}, no further interaction should be performed with this
- * detector. <br/>
- * If the state is {@link #STATE_KEYPHRASE_UNENROLLED} the caller may choose to begin
- * an enrollment flow for the keyphrase. <br/>
- * For {@value #STATE_KEYPHRASE_ENROLLED} a recognition can be started as desired. <br/>
- * If the return code is {@link #STATE_INVALID}, this detector is stale and must not be used.
- * A new detector should be obtained and used.
- *
- * @return Indicates if always-on hotword detection is available for the given keyphrase.
- * The return code is one of {@link #STATE_HARDWARE_UNAVAILABLE},
- * {@link #STATE_KEYPHRASE_UNSUPPORTED}, {@link #STATE_KEYPHRASE_UNENROLLED},
- * {@link #STATE_KEYPHRASE_ENROLLED}, or {@link #STATE_INVALID}.
- */
- public int getAvailability() {
- synchronized (mLock) {
- return internalGetAvailabilityLocked();
- }
+ new RefreshAvailabiltyTask().execute();
}
/**
* Gets the recognition modes supported by the associated keyphrase.
*
* @throws UnsupportedOperationException if the keyphrase itself isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int getSupportedRecognitionModes() {
synchronized (mLock) {
@@ -216,7 +214,9 @@
}
private int getSupportedRecognitionModesLocked() {
- if (mDisabled) {
+ // This method only makes sense if we can actually support a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED
+ && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
throw new UnsupportedOperationException(
"Getting supported recognition modes for the keyphrase is not supported");
}
@@ -232,8 +232,8 @@
* {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}.
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int startRecognition(int recognitionFlags) {
synchronized (mLock) {
@@ -242,7 +242,8 @@
}
private int startRecognitionLocked(int recognitionFlags) {
- if (internalGetAvailabilityLocked() != STATE_KEYPHRASE_ENROLLED) {
+ // This method only makes sense if we can start a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
@@ -273,8 +274,8 @@
*
* @return {@link #STATUS_OK} if the call succeeds, an error code otherwise.
* @throws UnsupportedOperationException if the recognition isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public int stopRecognition() {
synchronized (mLock) {
@@ -283,7 +284,8 @@
}
private int stopRecognitionLocked() {
- if (internalGetAvailabilityLocked() != STATE_KEYPHRASE_ENROLLED) {
+ // This method only makes sense if we can start a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
@@ -310,11 +312,13 @@
* {@link #MANAGE_ACTION_UN_ENROLL}.
* @return An {@link Intent} to manage the given keyphrase.
* @throws UnsupportedOperationException if managing they keyphrase isn't supported.
- * Callers should check the availability by calling {@link #getAvailability()}
- * before calling this method to avoid this exception.
+ * Callers should only call this method after a supported state callback on
+ * {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
*/
public Intent getManageIntent(int action) {
- if (mDisabled) {
+ // This method only makes sense if we can actually support a recognition.
+ if (mAvailability != STATE_KEYPHRASE_ENROLLED
+ && mAvailability != STATE_KEYPHRASE_UNENROLLED) {
throw new UnsupportedOperationException(
"Managing the given keyphrase is not supported");
}
@@ -327,34 +331,6 @@
return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
}
- private int internalGetAvailabilityLocked() {
- if (mInvalidated) {
- return STATE_INVALID;
- }
-
- ModuleProperties dspModuleProperties = null;
- try {
- dspModuleProperties =
- mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in getDspProperties!");
- }
- // No DSP available
- if (dspModuleProperties == null) {
- return STATE_HARDWARE_UNAVAILABLE;
- }
- // No enrollment application supports this keyphrase/locale
- if (mKeyphraseMetadata == null) {
- return STATE_KEYPHRASE_UNSUPPORTED;
- }
-
- // This keyphrase hasn't been enrolled.
- if (mEnrolledSoundModel == null) {
- return STATE_KEYPHRASE_UNENROLLED;
- }
- return STATE_KEYPHRASE_ENROLLED;
- }
-
/**
* Invalidates this hotword detector so that any future calls to this result
* in an IllegalStateException.
@@ -363,7 +339,8 @@
*/
void invalidate() {
synchronized (mLock) {
- mInvalidated = true;
+ mAvailability = STATE_INVALID;
+ notifyStateChangedLocked();
}
}
@@ -376,38 +353,22 @@
synchronized (mLock) {
// TODO: This should stop the recognition if it was using an enrolled sound model
// that's no longer available.
- if (mKeyphraseMetadata != null) {
- mEnrolledSoundModel = internalGetKeyphraseSoundModelLocked(mKeyphraseMetadata.id);
+ if (mAvailability == STATE_INVALID
+ || mAvailability == STATE_HARDWARE_UNAVAILABLE
+ || mAvailability == STATE_KEYPHRASE_UNSUPPORTED) {
+ Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config");
+ return;
}
+
+ // Execute a refresh availability task - which should then notify of a change.
+ new RefreshAvailabiltyTask().execute();
}
}
- /**
- * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
- */
- private KeyphraseSoundModel internalGetKeyphraseSoundModelLocked(int keyphraseId) {
- List<KeyphraseSoundModel> soundModels;
- try {
- soundModels = mModelManagementService
- .listRegisteredKeyphraseSoundModels(mVoiceInteractionService);
- if (soundModels == null || soundModels.isEmpty()) {
- Slog.i(TAG, "No available sound models for keyphrase ID: " + keyphraseId);
- return null;
- }
- for (KeyphraseSoundModel soundModel : soundModels) {
- if (soundModel.keyphrases == null) {
- continue;
- }
- for (Keyphrase keyphrase : soundModel.keyphrases) {
- if (keyphrase.id == keyphraseId) {
- return soundModel;
- }
- }
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!");
- }
- return null;
+ private void notifyStateChangedLocked() {
+ Message message = Message.obtain(mHandler, MSG_STATE_CHANGED);
+ message.arg1 = mAvailability;
+ message.sendToTarget();
}
/** @hide */
@@ -437,6 +398,9 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_STATE_CHANGED:
+ mExternalCallback.onAvailabilityChanged(msg.arg1);
+ break;
case MSG_HOTWORD_DETECTED:
mExternalCallback.onDetected((byte[]) msg.obj);
break;
@@ -447,4 +411,95 @@
}
}
}
+
+ class RefreshAvailabiltyTask extends AsyncTask<Void, Void, Void> {
+
+ @Override
+ public Void doInBackground(Void... params) {
+ int availability = internalGetInitialAvailability();
+ KeyphraseSoundModel soundModel = null;
+ // Fetch the sound model if the availability is one of the supported ones.
+ if (availability == STATE_NOT_READY
+ || availability == STATE_KEYPHRASE_UNENROLLED
+ || availability == STATE_KEYPHRASE_ENROLLED) {
+ soundModel =
+ internalGetKeyphraseSoundModel(mKeyphraseMetadata.id);
+ if (soundModel == null) {
+ availability = STATE_KEYPHRASE_UNENROLLED;
+ } else {
+ availability = STATE_KEYPHRASE_ENROLLED;
+ }
+ }
+
+ synchronized (mLock) {
+ if (DBG) {
+ Slog.d(TAG, "Hotword availability changed from " + mAvailability
+ + " -> " + availability);
+ }
+ mAvailability = availability;
+ mEnrolledSoundModel = soundModel;
+ notifyStateChangedLocked();
+ }
+ return null;
+ }
+
+ /**
+ * @return The initial availability without checking the enrollment status.
+ */
+ private int internalGetInitialAvailability() {
+ synchronized (mLock) {
+ // This detector has already been invalidated.
+ if (mAvailability == STATE_INVALID) {
+ return STATE_INVALID;
+ }
+ }
+
+ ModuleProperties dspModuleProperties = null;
+ try {
+ dspModuleProperties =
+ mModelManagementService.getDspModuleProperties(mVoiceInteractionService);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in getDspProperties!");
+ }
+ // No DSP available
+ if (dspModuleProperties == null) {
+ return STATE_HARDWARE_UNAVAILABLE;
+ }
+ // No enrollment application supports this keyphrase/locale
+ if (mKeyphraseMetadata == null) {
+ return STATE_KEYPHRASE_UNSUPPORTED;
+ }
+ return STATE_NOT_READY;
+ }
+
+ /**
+ * @return The corresponding {@link KeyphraseSoundModel} or null if none is found.
+ */
+ private KeyphraseSoundModel internalGetKeyphraseSoundModel(int keyphraseId) {
+ List<KeyphraseSoundModel> soundModels;
+ try {
+ soundModels = mModelManagementService
+ .listRegisteredKeyphraseSoundModels(mVoiceInteractionService);
+ if (soundModels == null || soundModels.isEmpty()) {
+ Slog.i(TAG, "No available sound models for keyphrase ID: " + keyphraseId);
+ return null;
+ }
+ for (int i = 0; i < soundModels.size(); i++) {
+ KeyphraseSoundModel soundModel = soundModels.get(i);
+ if (soundModel.keyphrases == null || soundModel.keyphrases.length == 0) {
+ continue;
+ }
+ for (int j = 0; i < soundModel.keyphrases.length; j++) {
+ Keyphrase keyphrase = soundModel.keyphrases[j];
+ if (keyphrase.id == keyphraseId) {
+ return soundModel;
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in listRegisteredKeyphraseSoundModels!");
+ }
+ return null;
+ }
+ }
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0c2ba26..82e23c4 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -193,17 +193,6 @@
mHotwordDetector.onSoundModelsChanged();
}
}
- onSoundModelsChanged();
- }
-
- /**
- * Called when the sound models available for recognition change.
- * This may be called if a new sound model is available or
- * an existing one is updated or removed.
- * Implementations must check the availability of the hotword detector if they own one
- * by calling {@link AlwaysOnHotwordDetector#getAvailability()} before calling into it.
- */
- public void onSoundModelsChanged() {
}
/**
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 0a1204d..73c73218 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -346,7 +346,9 @@
* Deep copies the data into native to simplify reference ownership.
*/
public boolean setOutline(Outline outline) {
- if (outline == null || outline.isEmpty()) {
+ if (outline == null) {
+ return nSetOutlineNone(mNativeRenderNode);
+ } else if (outline.isEmpty()) {
return nSetOutlineEmpty(mNativeRenderNode);
} else if (outline.mRect != null) {
return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
@@ -878,6 +880,7 @@
int right, int bottom, float radius);
private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath);
private static native boolean nSetOutlineEmpty(long renderNode);
+ private static native boolean nSetOutlineNone(long renderNode);
private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
private static native boolean nSetRevealClip(long renderNode,
boolean shouldClip, boolean inverseClip, float x, float y, float radius);
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 79557ad..71102e8 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -234,6 +234,8 @@
setSubtitle(subtitle);
}
+ // Set the default context, since setPopupTheme() may be a no-op.
+ mPopupContext = mContext;
setPopupTheme(a.getResourceId(R.styleable.Toolbar_popupTheme, 0));
a.recycle();
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 901d6e6..d91eb69 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -27,6 +27,10 @@
// be kept in sync with frameworks/native/include/binder/IBatteryStats.h
void noteStartSensor(int uid, int sensor);
void noteStopSensor(int uid, int sensor);
+ void noteStartVideo(int uid);
+ void noteStopVideo(int uid);
+ void noteStartAudio(int uid);
+ void noteStopAudio(int uid);
// Remaining methods are only used in Java.
byte[] getStatistics();
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 760ed45..32cf286 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -79,26 +79,6 @@
jmethodID setVendorInfo;
} dhcpResultsFieldIds;
-static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_enable(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
-static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
-{
- int result;
-
- const char *nameStr = env->GetStringUTFChars(ifname, NULL);
- result = ::ifc_disable(nameStr);
- env->ReleaseStringUTFChars(ifname, nameStr);
- return (jint)result;
-}
-
static jint android_net_utils_resetConnections(JNIEnv* env, jobject clazz,
jstring ifname, jint mask)
{
@@ -280,9 +260,6 @@
*/
static JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
-
- { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
- { "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
{ "resetConnections", "(Ljava/lang/String;I)I", (void *)android_net_utils_resetConnections },
{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcp },
{ "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z", (void *)android_net_utils_runDhcpRenew },
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index c6ae9cc..1f3909a 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -153,6 +153,14 @@
return true;
}
+static jboolean android_view_RenderNode_setOutlineNone(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr) {
+ RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ renderNode->mutateStagingProperties().mutableOutline().setNone();
+ renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
+}
+
static jboolean android_view_RenderNode_setClipToOutline(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean clipToOutline) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
@@ -486,6 +494,7 @@
{ "nSetOutlineRoundRect", "(JIIIIF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
{ "nSetOutlineConvexPath", "(JJ)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
+ { "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
{ "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
{ "nSetRevealClip", "(JZZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
diff --git a/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
index de5819d..de7ac29 100644
--- a/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
index f151bfb..bbf5928 100644
--- a/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
index c868ed1..d4bd169 100644
--- a/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
index 7cb1486..2e7bc12 100644
--- a/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png b/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
index 2ae7234..1086e9d 100644
--- a/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
+++ b/core/res/res/drawable-xxxhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 00a04ba..73c6a67 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4185,6 +4185,8 @@
<attr name="dropDownSelector" />
<!-- Background drawable to use for the dropdown in spinnerMode="dropdown". -->
<attr name="popupBackground" />
+ <!-- Window elevation to use for the dropdown in spinnerMode="dropdown". -->
+ <attr name="popupElevation" />
<!-- Vertical offset from the spinner widget for positioning the dropdown in
spinnerMode="dropdown". -->
<attr name="dropDownVerticalOffset" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index e79f223..a8974c6 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -543,6 +543,7 @@
<style name="Widget.Material.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
</style>
<style name="Widget.Material.CompoundButton" parent="Widget.CompoundButton"/>
@@ -663,6 +664,7 @@
<style name="Widget.Material.PopupWindow.ActionMode">
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
<item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
</style>
@@ -731,6 +733,7 @@
<item name="background">@drawable/spinner_background_material</item>
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupElevation">@dimen/floating_window_z</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1709558..3a4c2a7 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -155,6 +155,11 @@
* Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
*/
public void setRoundRect(int left, int top, int right, int bottom, float radius) {
+ if (left >= right || top >= bottom) {
+ setEmpty();
+ return;
+ }
+
if (mRect == null) mRect = new Rect();
mRect.set(left, top, right, bottom);
mRadius = radius;
@@ -173,11 +178,17 @@
* Sets the outline to the oval defined by input rect.
*/
public void setOval(int left, int top, int right, int bottom) {
+ if (left >= right || top >= bottom) {
+ setEmpty();
+ return;
+ }
+
if ((bottom - top) == (right - left)) {
// represent circle as round rect, for efficiency, and to enable clipping
setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f);
return;
}
+
if (mPath == null) mPath = new Path();
mPath.reset();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
@@ -196,6 +207,11 @@
* Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
*/
public void setConvexPath(@NonNull Path convexPath) {
+ if (convexPath.isEmpty()) {
+ setEmpty();
+ return;
+ }
+
if (!convexPath.isConvex()) {
throw new IllegalArgumentException("path must be convex");
}
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
index 024bdfd..83426e8 100644
--- a/libs/hwui/Outline.h
+++ b/libs/hwui/Outline.h
@@ -49,15 +49,20 @@
mBounds.set(outline->getBounds());
}
- bool isEmpty() const {
- return mType == kOutlineType_None;
+ void setEmpty() {
+ mType = kOutlineType_Empty;
+ mPath.reset();
}
- void setEmpty() {
+ void setNone() {
mType = kOutlineType_None;
mPath.reset();
}
+ bool isEmpty() const {
+ return mType == kOutlineType_Empty;
+ }
+
void setShouldClip(bool clip) {
mShouldClip = clip;
}
@@ -81,7 +86,7 @@
}
const SkPath* getPath() const {
- if (mType == kOutlineType_None) return NULL;
+ if (mType == kOutlineType_None || mType == kOutlineType_Empty) return NULL;
return &mPath;
}
@@ -89,8 +94,9 @@
private:
enum OutlineType {
kOutlineType_None = 0,
- kOutlineType_ConvexPath = 1,
- kOutlineType_RoundRect = 2
+ kOutlineType_Empty = 1,
+ kOutlineType_ConvexPath = 2,
+ kOutlineType_RoundRect = 3
};
bool mShouldClip;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fe03806..3eb779f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -581,7 +581,7 @@
template <class T>
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
- if (properties().getAlpha() <= 0.0f || properties().getOutline().isEmpty()) return;
+ if (properties().getAlpha() <= 0.0f || !properties().getOutline().getPath()) return;
mat4 shadowMatrixXY(transformFromParent);
applyViewPropertyTransforms(shadowMatrixXY);
@@ -776,16 +776,23 @@
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+ const int level = handler.level();
+ if (mDisplayListData->isEmpty()) {
+ DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
+
const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
// If we are updating the contents of mLayer, we don't want to apply any of
// the RenderNode's properties to this issueOperations pass. Those will all
// be applied when the layer is drawn, aka when this is true.
const bool useViewProperties = (!mLayer || drawLayer);
-
- const int level = handler.level();
- if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) {
- DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
- return;
+ if (useViewProperties) {
+ const Outline& outline = properties().getOutline();
+ if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", level * 2, "", this, getName());
+ return;
+ }
}
handler.startMark(getName());
diff --git a/media/java/android/media/browse/IMediaBrowserService.aidl b/media/java/android/media/browse/IMediaBrowserService.aidl
index 4b2cb9d..177bd1b 100644
--- a/media/java/android/media/browse/IMediaBrowserService.aidl
+++ b/media/java/android/media/browse/IMediaBrowserService.aidl
@@ -2,6 +2,7 @@
package android.media.browse;
+import android.content.res.Configuration;
import android.media.browse.IMediaBrowserServiceCallbacks;
import android.net.Uri;
import android.os.Bundle;
@@ -17,4 +18,5 @@
void addSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(in Uri uri, IMediaBrowserServiceCallbacks callbacks);
+ void loadThumbnail(in Uri uri, int width, int height, IMediaBrowserServiceCallbacks callbacks);
}
\ No newline at end of file
diff --git a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
index ead7624..1f03a1a 100644
--- a/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/media/browse/IMediaBrowserServiceCallbacks.aidl
@@ -3,8 +3,10 @@
package android.media.browse;
import android.content.pm.ParceledListSlice;
+import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.net.Uri;
+import android.os.Bundle;
/**
* Media API allows clients to browse through hierarchy of a user’s media collection,
@@ -16,9 +18,11 @@
* Invoked when the connected has been established.
* @param root The root Uri for browsing.
* @param session The {@link MediaSession.Token media session token} that can be used to control
- * the playback of the media app.
+ * the playback of the media app.
+ * @param extra Extras returned by the media service.
*/
- void onConnect(in Uri root, in MediaSession.Token session);
+ void onConnect(in Uri root, in MediaSession.Token session, in Bundle extras);
void onConnectFailed();
void onLoadChildren(in Uri uri, in ParceledListSlice list);
+ void onLoadThumbnail(in Uri uri, int width, int height, in Bitmap bitmap);
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index beec5f9..d17f95d 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -17,11 +17,13 @@
package android.media.browse;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ParceledListSlice;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.net.Uri;
@@ -36,7 +38,9 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Browses media content offered by a link MediaBrowserService.
@@ -61,6 +65,8 @@
private final Handler mHandler = new Handler();
private final ArrayMap<Uri,Subscription> mSubscriptions =
new ArrayMap<Uri, MediaBrowser.Subscription>();
+ private final ArrayMap<ThumbnailRequest, HashSet<ThumbnailCallback>> mThumbnailCallbacks =
+ new ArrayMap<ThumbnailRequest, HashSet<ThumbnailCallback>>();
private int mState = CONNECT_STATE_DISCONNECTED;
private MediaServiceConnection mServiceConnection;
@@ -68,6 +74,7 @@
private IMediaBrowserServiceCallbacks mServiceCallbacks;
private Uri mRootUri;
private MediaSession.Token mMediaSessionToken;
+ private Bundle mExtras;
/**
* Creates a media browser for the specified media browse service.
@@ -162,7 +169,6 @@
/**
* Disconnects from the media browse service.
- * @more
* After this, no more callbacks will be received.
*/
public void disconnect() {
@@ -223,7 +229,7 @@
* </p>
*
* @throws IllegalStateException if not connected.
- */
+ */
public @NonNull Uri getRoot() {
if (mState != CONNECT_STATE_CONNECTED) {
throw new IllegalStateException("getSessionToken() called while not connected (state="
@@ -233,6 +239,19 @@
}
/**
+ * Gets any extras for the media service.
+ *
+ * @throws IllegalStateException if not connected.
+ */
+ public @Nullable Bundle getExtras() {
+ if (mState != CONNECT_STATE_CONNECTED) {
+ throw new IllegalStateException("getExtras() called while not connected (state="
+ + getStateLabel(mState) + ")");
+ }
+ return mExtras;
+ }
+
+ /**
* Gets the media session token associated with the media browser.
* <p>
* Note that the session token may become invalid or change when when the
@@ -332,18 +351,45 @@
/**
* Loads the thumbnail of a media item.
*
- * @param uri The uri of the media item.
+ * @param uri The uri of the thumbnail.
* @param width The preferred width of the icon in dp.
* @param height The preferred width of the icon in dp.
- * @param density The preferred density of the icon. Must be one of the android
- * density buckets.
* @param callback The callback to receive the thumbnail.
- *
- * @throws IllegalStateException if not connected. TODO: Is this restriction necessary?
*/
- public void loadThumbnail(@NonNull Uri uri, int width, int height, int density,
- @NonNull ThumbnailCallback callback) {
- throw new RuntimeException("implement me");
+ public void loadThumbnail(final @NonNull Uri uri, final int width, final int height,
+ final @NonNull ThumbnailCallback callback) {
+ if (uri == null) {
+ throw new IllegalArgumentException("thumbnail uri cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("thumbnail callback cannot be null");
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ HashSet<ThumbnailCallback> callbackSet;
+ ThumbnailRequest request = new ThumbnailRequest(uri, width, height);
+ callbackSet = mThumbnailCallbacks.get(request);
+ if (callbackSet != null) {
+ callbackSet.add(callback);
+ mThumbnailCallbacks.put(request, callbackSet);
+ // same request has been sent. we will wait for the callback.
+ return;
+ }
+ callbackSet = new HashSet<ThumbnailCallback>();
+ callbackSet.add(callback);
+ mThumbnailCallbacks.put(request, callbackSet);
+ if (mState == CONNECT_STATE_CONNECTED) {
+ try {
+ mServiceBinder.loadThumbnail(uri, width, height, mServiceCallbacks);
+ } catch (RemoteException e) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reload the thumbnails. So nothing to do here.
+ Log.d(TAG, "loadThumbnail failed with RemoteException uri=" + uri);
+ }
+ }
+ }
+ });
}
/**
@@ -365,7 +411,7 @@
}
private final void onServiceConnected(final IMediaBrowserServiceCallbacks callback,
- final Uri root, final MediaSession.Token session) {
+ final Uri root, final MediaSession.Token session, final Bundle extra) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -382,6 +428,7 @@
}
mRootUri = root;
mMediaSessionToken = session;
+ mExtras = extra;
mState = CONNECT_STATE_CONNECTED;
if (DBG) {
@@ -402,6 +449,17 @@
}
}
+ for (ThumbnailRequest request : mThumbnailCallbacks.keySet()) {
+ try {
+ mServiceBinder.loadThumbnail(request.uri, request.width, request.height,
+ mServiceCallbacks);
+ } catch (RemoteException e) {
+ // Process is crashing. We will disconnect, and upon reconnect we will
+ // automatically reload. So nothing to do here.
+ Log.d(TAG, "loadThumbnail failed with RemoteException uri=" + request.uri);
+ }
+ }
+
}
});
}
@@ -468,6 +526,31 @@
});
}
+ private final void onLoadThumbnail(final IMediaBrowserServiceCallbacks callback,
+ final ThumbnailRequest request, final Bitmap bitmap) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Check that there hasn't been a disconnect or a different
+ // ServiceConnection.
+ if (!isCurrent(callback, "onLoadThumbnail")) {
+ return;
+ }
+
+ Set<ThumbnailCallback> callbackSet = mThumbnailCallbacks.get(request);
+ if (callbackSet == null) {
+ Log.d(TAG, "onLoadThumbnail called for request=" + request +
+ " but the callback is not registered");
+ return;
+ }
+ for (ThumbnailCallback thumbnailCallback : callbackSet) {
+ thumbnailCallback.onThumbnailLoaded(request.uri, bitmap);
+ }
+ mThumbnailCallbacks.remove(request);
+ }
+ });
+ }
+
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
@@ -567,6 +650,51 @@
}
}
+ private static class ThumbnailRequest {
+ Uri uri;
+ int width;
+ int height;
+ ThumbnailRequest(@NonNull Uri uri, int width, int height) {
+ if (uri == null) {
+ throw new IllegalArgumentException("thumbnail uri cannot be null");
+ }
+ this.uri = uri;
+ this.width = width;
+ this.height = height;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ThumbnailRequest)) return false;
+
+ ThumbnailRequest that = (ThumbnailRequest) o;
+
+ if (height != that.height) return false;
+ if (width != that.width) return false;
+ if (!uri.equals(that.uri)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = uri.hashCode();
+ result = 31 * result + width;
+ result = 31 * result + height;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ThumbnailRequest{" +
+ "uri=" + uri +
+ ", width=" + width +
+ ", height=" + height +
+ '}';
+ }
+ }
+
/**
* ServiceConnection to the other app.
*/
@@ -666,10 +794,11 @@
* are the initial data as requested.
*/
@Override
- public void onConnect(final Uri root, final MediaSession.Token session) {
+ public void onConnect(final Uri root, final MediaSession.Token session,
+ final Bundle extras) {
MediaBrowser mediaBrowser = mMediaBrowser.get();
if (mediaBrowser != null) {
- mediaBrowser.onServiceConnected(this, root, session);
+ mediaBrowser.onServiceConnected(this, root, session, extras);
}
}
@@ -691,6 +820,15 @@
mediaBrowser.onLoadChildren(this, uri, list);
}
}
+
+ @Override
+ public void onLoadThumbnail(final Uri uri, int width, int height, final Bitmap bitmap) {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ ThumbnailRequest request = new ThumbnailRequest(uri, width, height);
+ mediaBrowser.onLoadThumbnail(this, request, bitmap);
+ }
+ }
}
private static class Subscription {
diff --git a/media/java/android/media/browse/MediaBrowserItem.java b/media/java/android/media/browse/MediaBrowserItem.java
index 119f687..38e765f 100644
--- a/media/java/android/media/browse/MediaBrowserItem.java
+++ b/media/java/android/media/browse/MediaBrowserItem.java
@@ -32,6 +32,8 @@
*/
public final class MediaBrowserItem implements Parcelable {
private final Uri mUri;
+ private final Uri mIconUri;
+ private final int mIconResId;
private final int mFlags;
private final CharSequence mTitle;
private final CharSequence mSummary;
@@ -59,8 +61,8 @@
/**
* Initialize a MediaBrowserItem object.
*/
- private MediaBrowserItem(@NonNull Uri uri, int flags, @NonNull CharSequence title,
- CharSequence summary, Bundle extras) {
+ private MediaBrowserItem(@NonNull Uri uri, @Nullable Uri iconUri, int iconResId, int flags,
+ @NonNull CharSequence title, CharSequence summary, Bundle extras) {
if (uri == null) {
throw new IllegalArgumentException("uri can not be null");
}
@@ -68,6 +70,8 @@
throw new IllegalArgumentException("title can not be null");
}
mUri = uri;
+ mIconUri = iconUri;
+ mIconResId = iconResId;
mFlags = flags;
mTitle = title;
mSummary = summary;
@@ -79,6 +83,8 @@
*/
private MediaBrowserItem(Parcel in) {
mUri = Uri.CREATOR.createFromParcel(in);
+ mIconUri = Uri.CREATOR.createFromParcel(in);
+ mIconResId = in.readInt();
mFlags = in.readInt();
mTitle = in.readCharSequence();
if (in.readInt() != 0) {
@@ -101,6 +107,8 @@
@Override
public void writeToParcel(Parcel out, int flags) {
mUri.writeToParcel(out, flags);
+ mIconUri.writeToParcel(out, flags);
+ out.writeInt(mIconResId);
out.writeInt(mFlags);
out.writeCharSequence(mTitle);
if (mSummary != null) {
@@ -138,6 +146,20 @@
}
/**
+ * Gets the Uri of the icon.
+ */
+ public @Nullable Uri getIconUri() {
+ return mIconUri;
+ }
+
+ /**
+ * Gets the resource id of the icon.
+ */
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ /**
* Gets the flags of the item.
*/
public @Flags int getFlags() {
@@ -195,6 +217,8 @@
private final Uri mUri;
private final int mFlags;
private final CharSequence mTitle;
+ private Uri mIconUri;
+ private int mIconResId;
private CharSequence mSummary;
private Bundle mExtras;
@@ -214,6 +238,26 @@
}
/**
+ * Sets the uri of the icon.
+ * <p>
+ * If both {@link #setIconUri(Uri)} and {@link #setIconResId(int)} are called,
+ * the resource id will be used to load the icon.
+ * </p>
+ */
+ public @NonNull Builder setIconUri(@Nullable Uri iconUri) {
+ mIconUri = iconUri;
+ return this;
+ }
+
+ /**
+ * Sets the resource id of the icon.
+ */
+ public @NonNull Builder setIconResId(int resId) {
+ mIconResId = resId;
+ return this;
+ }
+
+ /**
* Sets summary of the item, or null if none.
*/
public @NonNull Builder setSummary(@Nullable CharSequence summary) {
@@ -234,7 +278,8 @@
* Builds the item.
*/
public @NonNull MediaBrowserItem build() {
- return new MediaBrowserItem(mUri, mFlags, mTitle, mSummary, mExtras);
+ return new MediaBrowserItem(mUri, mIconUri, mIconResId,
+ mFlags, mTitle, mSummary, mExtras);
}
}
}
diff --git a/media/java/android/media/browse/MediaBrowserService.java b/media/java/android/media/browse/MediaBrowserService.java
index ceb4b03..57befe7 100644
--- a/media/java/android/media/browse/MediaBrowserService.java
+++ b/media/java/android/media/browse/MediaBrowserService.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.net.Uri;
@@ -43,6 +44,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* Base class for media browse services.
@@ -81,7 +83,7 @@
String pkg;
Bundle rootHints;
IMediaBrowserServiceCallbacks callbacks;
- Uri root;
+ BrowserRoot root;
HashSet<Uri> subscriptions = new HashSet();
}
@@ -130,7 +132,8 @@
} else {
try {
mConnections.put(b, connection);
- callbacks.onConnect(connection.root, mSession);
+ callbacks.onConnect(connection.root.getRootUri(),
+ mSession, connection.root.getExtras());
} catch (RemoteException ex) {
Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ "pkg=" + pkg);
@@ -199,6 +202,22 @@
}
});
}
+
+ @Override
+ public void loadThumbnail(final Uri uri, final int width, final int height,
+ final IMediaBrowserServiceCallbacks callbacks) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Bitmap bitmap = onGetThumbnail(uri, width, height);
+ try {
+ callbacks.onLoadThumbnail(uri, width, height, bitmap);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in calling onLoadThumbnail", e);
+ }
+ }
+ });
+ }
}
@Override
@@ -220,7 +239,7 @@
}
/**
- * Called to get the root uri for browsing by a particular client.
+ * Called to get the root information for browsing by a particular client.
* <p>
* The implementation should verify that the client package has
* permission to access browse media information before returning
@@ -237,8 +256,8 @@
* for browsing, or null if none. The contents of this bundle may affect
* the information returned when browsing.
*/
- public abstract @Nullable Uri onGetRoot(@NonNull String clientPackageName, int clientUid,
- @Nullable Bundle rootHints);
+ protected abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
+ int clientUid, @Nullable Bundle rootHints);
/**
* Called to get information about the children of a media item.
@@ -247,7 +266,7 @@
* children are to be queried.
* @return The list of children, or null if the uri is invalid.
*/
- public abstract @Nullable List<MediaBrowserItem> onLoadChildren(@NonNull Uri parentUri);
+ protected abstract @Nullable List<MediaBrowserItem> onLoadChildren(@NonNull Uri parentUri);
/**
* Called to get the thumbnail of a particular media item.
@@ -255,14 +274,11 @@
* @param uri The uri of the media item.
* @param width The requested width of the icon in dp.
* @param height The requested height of the icon in dp.
- * @param density The requested density of the icon. This is the approximate density of the
- * screen on which the icon will be displayed. This density will be one of
- * the android density buckets.
+ *
* @return The file descriptor of the thumbnail, which may then be loaded
* using a bitmap factory, or null if the item does not have a thumbnail.
*/
- public abstract @Nullable Bitmap onGetThumbnail(@NonNull Uri uri,
- int width, int height, int density);
+ protected abstract @Nullable Bitmap onGetThumbnail(@NonNull Uri uri, int width, int height);
/**
* Call to set the media session.
@@ -288,15 +304,6 @@
}
/**
- * Notifies all connected media browsers that the content of
- * the browse service has changed in some way.
- * This will cause browsers to fetch subscribed content again.
- */
- public void notifyChange() {
- throw new RuntimeException("implement me");
- }
-
- /**
* Notifies all connected media browsers that the children of
* the specified Uri have changed in some way.
* This will cause browsers to fetch subscribed content again.
@@ -305,7 +312,19 @@
* children changed.
*/
public void notifyChildrenChanged(@NonNull Uri parentUri) {
- throw new RuntimeException("implement me");
+ if (parentUri == null) {
+ throw new IllegalArgumentException("parentUri cannot be null in notifyChildrenChanged");
+ }
+ for (IBinder binder : mConnections.keySet()) {
+ ConnectionRecord connection = mConnections.get(binder);
+ Set<Uri> uris = connection.subscriptions;
+ for (Uri uri : uris) {
+ if (uri.equals(parentUri)) {
+ performLoadChildren(uri, connection);
+ break;
+ }
+ }
+ }
}
/**
@@ -360,4 +379,25 @@
+ " package=" + connection.pkg);
}
}
+
+ public static class BrowserRoot {
+ final private Uri mUri;
+ final private Bundle mExtras;
+ public BrowserRoot(@NonNull Uri uri, @Nullable Bundle extras) {
+ if (uri == null) {
+ throw new IllegalArgumentException("The root uri in BrowserRoot cannot be null. " +
+ "Use null for BrowserRoot instead.");
+ }
+ mUri = uri;
+ mExtras = extras;
+ }
+
+ Uri getRootUri() {
+ return mUri;
+ }
+
+ Bundle getExtras() {
+ return mExtras;
+ }
+ }
}
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 627f11c..e9fe09e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -23,6 +23,7 @@
<color name="system_bar_background_opaque">#ff000000</color>
<color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black -->
<color name="system_bar_background_transparent">#00000000</color>
+ <color name="system_bar_background_warning">#fff4511e</color><!-- deep orange 600 -->
<color name="notification_panel_solid_background">#ff000000</color>
<drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable>
<color name="status_bar_recents_app_label_color">#ffffffff</color>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index fbfad5c..fd636ed 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -22,6 +22,7 @@
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
@@ -52,7 +53,6 @@
boolean mIsFullscreen;
- Paint mLayerPaint = new Paint();
static Paint sHighlightPaint;
public TaskBarView(Context context) {
@@ -167,7 +167,11 @@
mActivityDescription.setText(t.activityLabel);
}
// Try and apply the system ui tint
- setBackgroundColor(t.colorPrimary);
+ int existingBgColor = (getBackground() instanceof ColorDrawable) ?
+ ((ColorDrawable) getBackground()).getColor() : 0;
+ if (existingBgColor != t.colorPrimary) {
+ setBackgroundColor(t.colorPrimary);
+ }
mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
@@ -252,14 +256,4 @@
mDismissButton.setAlpha(1f);
}
}
-
- /** Enable the hw layers on this task view */
- void enableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- }
-
- /** Disable the hw layers on this task view */
- void disableHwLayers() {
- mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index d84a40f..f135e32 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -100,13 +100,15 @@
LayoutInflater mInflater;
// A convenience runnable to return all views to the pool
- // XXX: After this is set, we should mark this task stack view as disabled and check that in synchronize model
Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
@Override
public void run() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
- mViewPool.returnViewToPool((TaskView) getChildAt(i));
+ TaskView tv = (TaskView) getChildAt(i);
+ mViewPool.returnViewToPool(tv);
+ // Also hide the view since we don't need it anymore
+ tv.setVisibility(View.INVISIBLE);
}
}
};
@@ -133,33 +135,6 @@
}
}
});
- mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
- @Override
- public void run() {
- // Enable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.enableHwLayers();
- }
- }
- }, new Runnable() {
- @Override
- public void run() {
- // Disable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.disableHwLayers();
- }
- }
- }, new Runnable() {
- @Override
- public void run() {
- new Throwable("Invalid hw layers ref count").printStackTrace();
- Console.logError(getContext(), "Invalid HW layers ref count");
- }
- });
}
/** Sets the callbacks */
@@ -321,8 +296,8 @@
if (transform.t < 0) {
mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(0), stackScroll, mTmpTransform);
} else {
- mTmpTransform = mStackAlgorithm.getStackTransform(tasks.get(Math.min(tasks.size() - 1, visibleRange[0] + 1)),
- stackScroll, mTmpTransform);
+ int nextTaskStackScroll = mStackAlgorithm.getStackScrollForTaskIndex(task, 1);
+ mStackAlgorithm.getStackTransform(nextTaskStackScroll, stackScroll, mTmpTransform);
}
tv.updateViewPropertiesToTaskTransform(mTmpTransform, 0);
}
@@ -435,17 +410,8 @@
int curScroll = getStackScroll();
int newScroll = Math.max(mMinScroll, Math.min(mMaxScroll, curScroll));
if (newScroll != curScroll) {
- // Enable hw layers on the stack
- addHwLayersRefCount("animateBoundScroll");
-
// Start a new scroll animation
- animateScroll(curScroll, newScroll, new Runnable() {
- @Override
- public void run() {
- // Disable hw layers on the stack
- decHwLayersRefCount("animateBoundScroll");
- }
- });
+ animateScroll(curScroll, newScroll, null);
}
return mScrollAnimator;
}
@@ -490,8 +456,6 @@
if (!mScroller.isFinished()) {
// Abort the scroller
mScroller.abortAnimation();
- // And disable hw layers on the stack
- decHwLayersRefCount("flingScroll");
}
}
@@ -615,17 +579,6 @@
focusTask(mFocusedTaskIndex, true);
}
- /** Enables the hw layers and increments the hw layer requirement ref count */
- void addHwLayersRefCount(String reason) {
- mHwLayersTrigger.increment();
- }
-
- /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
- need them anymore. */
- void decHwLayersRefCount(String reason) {
- mHwLayersTrigger.decrement();
- }
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mTouchHandler.onInterceptTouchEvent(ev);
@@ -641,11 +594,6 @@
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
invalidate();
-
- // If we just finished scrolling, then disable the hw layers
- if (mScroller.isFinished()) {
- decHwLayersRefCount("finishedFlingScroll");
- }
}
}
@@ -969,9 +917,6 @@
// Detach the view from the hierarchy
detachViewFromParent(tv);
- // Disable HW layers
- tv.disableHwLayers();
-
// Reset the view properties
tv.resetViewProperties();
}
@@ -1015,11 +960,6 @@
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
}
-
- // Enable hw layers on this view if hw layers are enabled on the stack
- if (mHwLayersTrigger.getCount() > 0) {
- tv.enableHwLayers();
- }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 3c89cd7..7f94a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -103,12 +103,16 @@
transformOut.reset();
return transformOut;
}
+ return getStackTransform(getStackScrollForTaskIndex(task), stackScroll, transformOut);
+ }
+ /** Update/get the transform */
+ public TaskViewTransform getStackTransform(int taskStackScroll, int stackScroll, TaskViewTransform transformOut) {
// Map the items to an continuous position relative to the specified scroll
int numPeekCards = StackPeekNumCards;
float overlapHeight = StackOverlapPct * mTaskRect.height();
float peekHeight = StackPeekHeightPct * mStackRect.height();
- float t = (getStackScrollForTaskIndex(task) - stackScroll) / overlapHeight;
+ float t = (taskStackScroll - stackScroll) / overlapHeight;
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
@@ -116,8 +120,8 @@
float minScale = StackPeekMinScale;
float scaleRange = 1f - minScale;
float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
- float scale = Math.max(minScale, Math.min(1f, minScale +
- ((boundedT + (numPeekCards + 1)) * scaleInc)));
+ float scale = Math.max(minScale, Math.min(1f, minScale +
+ ((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
// Account for the bar offsets being scaled?
float scaleBarYOffset = (1f - scale) * mConfig.taskBarHeight;
@@ -169,6 +173,14 @@
}
/**
+ * Returns the scroll to such that the task transform at that task + index will have t=0.
+ * (If the scroll is not bounded)
+ */
+ int getStackScrollForTaskIndex(Task t, int relativeIndexOffset) {
+ return mTaskOffsetMap.get(t.key) + (int) (relativeIndexOffset * getTaskOverlapHeight());
+ }
+
+ /**
* Updates the cache of tasks to offsets.
*/
void updateTaskOffsets(ArrayList<Task> tasks) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 191dc37..b83f9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -148,8 +148,6 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
}
mLastMotionX = x;
@@ -160,10 +158,6 @@
case MotionEvent.ACTION_UP: {
// Animate the scroll back if we've cancelled
mSv.animateBoundScroll();
- // Disable HW layers
- if (mIsScrolling) {
- mSv.decHwLayersRefCount("stackScroll");
- }
// Reset the drag state and the velocity tracker
mIsScrolling = false;
mActivePointerId = INACTIVE_POINTER_ID;
@@ -241,8 +235,6 @@
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
}
}
if (mIsScrolling) {
@@ -271,8 +263,6 @@
int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
- // Enable HW layers on the stack
- mSv.addHwLayersRefCount("flingScroll");
// XXX: Make this animation a function of the velocity AND distance
int overscrollRange = (int) (Math.min(1f,
Math.abs((float) velocity / mMaximumVelocity)) *
@@ -291,10 +281,6 @@
mSv.animateBoundScroll();
}
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
mActivePointerId = INACTIVE_POINTER_ID;
mIsScrolling = false;
mTotalScrollMotion = 0;
@@ -315,10 +301,6 @@
break;
}
case MotionEvent.ACTION_CANCEL: {
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
// XXX: Make this animation a function of the velocity OR distance
@@ -351,8 +333,6 @@
TaskView tv = (TaskView) v;
// Disable clipping with the stack while we are swiping
tv.setClipViewInStack(false);
- // Enable HW layers on that task
- tv.enableHwLayers();
// Disallow touch events from this task view
tv.setTouchEnabled(false);
// Hide the footer
@@ -372,10 +352,6 @@
@Override
public void onChildDismissed(View v) {
TaskView tv = (TaskView) v;
- // Disable HW layers on that task
- if (mSv.mHwLayersTrigger.getCount() == 0) {
- tv.disableHwLayers();
- }
// Re-enable clipping with the stack (we will reuse this view)
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
@@ -387,10 +363,6 @@
@Override
public void onSnapBackCompleted(View v) {
TaskView tv = (TaskView) v;
- // Disable HW layers on that task
- if (mSv.mHwLayersTrigger.getCount() == 0) {
- tv.disableHwLayers();
- }
// Re-enable clipping with the stack
tv.setClipViewInStack(true);
// Re-enable touch events from this task view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index d1b33f3..259706b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,7 +22,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
@@ -34,8 +37,6 @@
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
-// XXX: In debug mode, we should override invalidate() and check the layout type (do this in TaskStackView as well)
-
/* A task view */
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
TaskFooterView.TaskFooterViewCallbacks, View.OnClickListener, View.OnLongClickListener {
@@ -53,6 +54,7 @@
int mDim;
int mMaxDim;
AccelerateInterpolator mDimInterpolator = new AccelerateInterpolator();
+ PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.MULTIPLY);
Task mTask;
boolean mTaskDataLoaded;
@@ -96,9 +98,8 @@
mMaxDim = mConfig.taskStackMaxDim;
mClipViewInStack = true;
mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx);
- setWillNotDraw(false);
- setDim(getDim());
setOutlineProvider(mViewBounds);
+ setDim(getDim());
}
/** Set callback */
@@ -164,7 +165,7 @@
}
// Apply the transform
- toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator,
+ toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false,
mUpdateDimListener);
}
@@ -262,7 +263,6 @@
AlternateRecentsComponent.consumeLastScreenshot();
}
})
- .withLayer()
.start();
} else {
// Otherwise, just enable the thumbnail clip
@@ -316,7 +316,6 @@
.setUpdateListener(null)
.setInterpolator(mConfig.quintOutInterpolator)
.setDuration(mConfig.taskViewEnterFromHomeDuration)
- .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -348,7 +347,6 @@
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutLinearInInterpolator)
.setDuration(mConfig.taskViewExitToHomeDuration)
- .withLayer()
.withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
ctx.postAnimationTrigger.increment();
@@ -384,7 +382,6 @@
.setUpdateListener(null)
.setInterpolator(mConfig.fastOutSlowInInterpolator)
.setDuration(mConfig.taskViewRemoveAnimDuration)
- .withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -466,7 +463,10 @@
/** Returns the current dim. */
public void setDim(int dim) {
mDim = dim;
- postInvalidateOnAnimation();
+ int inverse = 255 - mDim;
+ mDimColorFilter.setColor(Color.argb(0xFF, inverse, inverse, inverse));
+ mLayerPaint.setColorFilter(mDimColorFilter);
+ setLayerType(LAYER_TYPE_HARDWARE, mLayerPaint);
}
/** Returns the current dim. */
@@ -491,16 +491,6 @@
/**** View drawing ****/
@Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
-
- // Apply the dim if necessary
- if (mDim > 0) {
- canvas.drawColor(mDim << 24);
- }
- }
-
- @Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (mIsStub && (child != mBarView)) {
// Skip the thumbnail view if we are in stub mode
@@ -509,20 +499,6 @@
return super.drawChild(canvas, child, drawingTime);
}
- /** Enable the hw layers on this task view */
- void enableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- mBarView.enableHwLayers();
- mFooterView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
- }
-
- /** Disable the hw layers on this task view */
- void disableHwLayers() {
- mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- mBarView.disableHwLayers();
- mFooterView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
- }
-
/**** View focus state ****/
/**
@@ -640,7 +616,7 @@
/**** View.OnClickListener Implementation ****/
@Override
- public void onClick(final View v) {
+ public void onClick(final View v) {
// We purposely post the handler delayed to allow for the touch feedback to draw
final TaskView tv = this;
postDelayed(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index d583c20..aeb4fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -83,12 +83,12 @@
}
/** Applies this transform to a view. */
- public void applyToTaskView(View v, int duration, Interpolator interp,
+ public void applyToTaskView(View v, int duration, Interpolator interp, boolean allowLayers,
ValueAnimator.AnimatorUpdateListener scaleUpdateListener) {
// Check to see if any properties have changed, and update the task view
if (duration > 0) {
ViewPropertyAnimator anim = v.animate();
- boolean useLayers = false;
+ boolean requiresLayers = false;
// Animate to the final state
if (hasTranslationYChangedFrom(v.getTranslationY())) {
@@ -102,14 +102,14 @@
anim.scaleX(scale)
.scaleY(scale)
.setUpdateListener(scaleUpdateListener);
- useLayers = true;
+ requiresLayers = true;
}
if (hasAlphaChangedFrom(v.getAlpha())) {
// Use layers if we animate alpha
anim.alpha(alpha);
- useLayers = true;
+ requiresLayers = true;
}
- if (useLayers) {
+ if (requiresLayers && allowLayers) {
anim.withLayer();
}
anim.setStartDelay(startDelay)
@@ -146,7 +146,6 @@
v.setScaleX(1f);
v.setScaleY(1f);
v.setAlpha(1f);
- v.invalidate();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index a41ec22..303454b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -44,6 +44,7 @@
public static final int MODE_TRANSLUCENT = 2;
public static final int MODE_LIGHTS_OUT = 3;
public static final int MODE_TRANSPARENT = 4;
+ public static final int MODE_WARNING = 5;
public static final int LIGHTS_IN_DURATION = 250;
public static final int LIGHTS_OUT_DURATION = 750;
@@ -100,6 +101,7 @@
if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT";
if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT";
+ if (mode == MODE_WARNING) return "MODE_WARNING";
throw new IllegalArgumentException("Unknown mode " + mode);
}
@@ -115,6 +117,7 @@
private final int mOpaque;
private final int mSemiTransparent;
private final int mTransparent;
+ private final int mWarning;
private final Drawable mGradient;
private final TimeInterpolator mInterpolator;
@@ -135,10 +138,12 @@
mOpaque = 0xff0000ff;
mSemiTransparent = 0x7f0000ff;
mTransparent = 0x2f0000ff;
+ mWarning = 0xffff0000;
} else {
mOpaque = res.getColor(R.color.system_bar_background_opaque);
mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent);
mTransparent = res.getColor(R.color.system_bar_background_transparent);
+ mWarning = res.getColor(R.color.system_bar_background_warning);
}
mGradient = res.getDrawable(gradientResourceId);
mInterpolator = new LinearInterpolator();
@@ -189,7 +194,9 @@
@Override
public void draw(Canvas canvas) {
int targetGradientAlpha = 0, targetColor = 0;
- if (mMode == MODE_TRANSLUCENT) {
+ if (mMode == MODE_WARNING) {
+ targetColor = mWarning;
+ } else if (mMode == MODE_TRANSLUCENT) {
targetColor = mSemiTransparent;
} else if (mMode == MODE_SEMI_TRANSPARENT) {
targetColor = mSemiTransparent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 59becd3..e4e67c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -28,6 +28,8 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
@@ -125,6 +127,7 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -705,6 +708,16 @@
// Other icons
mLocationController = new LocationControllerImpl(mContext); // will post a notification
mBatteryController = new BatteryController(mContext);
+ mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
+ @Override
+ public void onPowerSaveChanged() {
+ mHandler.post(mCheckBarModes);
+ }
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ // noop
+ }
+ });
mNetworkController = new NetworkControllerImpl(mContext);
mHotspotController = new HotspotControllerImpl(mContext);
mBluetoothController = new BluetoothControllerImpl(mContext);
@@ -2310,7 +2323,12 @@
}
private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
- final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN;
+ final boolean powerSave = mBatteryController.isPowerSave();
+ final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
+ && !powerSave;
+ if (powerSave && getBarState() != StatusBarState.KEYGUARD) {
+ mode = MODE_WARNING;
+ }
transitions.transitionTo(mode, anim);
}
@@ -2325,7 +2343,8 @@
@Override
public void run() {
checkBarModes();
- }};
+ }
+ };
@Override
public void setInteracting(int barWindow, boolean interacting) {
@@ -2644,6 +2663,9 @@
if (mUserSwitcherController != null) {
mUserSwitcherController.dump(fd, pw, args);
}
+ if (mBatteryController != null) {
+ mBatteryController.dump(fd, pw, args);
+ }
}
private String hunStateToString(Entry entry) {
@@ -3111,6 +3133,8 @@
int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
"translucent".equals(mode) ? MODE_TRANSLUCENT :
"semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
+ "transparent".equals(mode) ? MODE_TRANSPARENT :
+ "warning".equals(mode) ? MODE_WARNING :
-1;
if (barMode != -1) {
boolean animate = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index b2709bc..ffb4dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -363,6 +363,11 @@
}
}
+ @Override
+ public void onPowerSaveChanged() {
+ // could not care less
+ }
+
private void updateClickTargets() {
setClickable(!mKeyguardShowing || mExpanded);
mDateTime.setClickable(mExpanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index ccfe18d..1e65543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,41 +16,49 @@
package com.android.systemui.statusbar.policy;
-import com.android.internal.app.IBatteryStats;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBarHeaderView;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
-import android.os.BatteryStats;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.text.format.Formatter;
+import android.os.PowerManager;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
public class BatteryController extends BroadcastReceiver {
- private static final String TAG = "StatusBar.BatteryController";
+ private static final String TAG = "BatteryController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+ private final PowerManager mPowerManager;
private int mLevel;
private boolean mPluggedIn;
private boolean mCharging;
private boolean mCharged;
-
- public interface BatteryStateChangeCallback {
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
- }
+ private boolean mPowerSave;
public BatteryController(Context context) {
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
context.registerReceiver(this, filter);
+
+ updatePowerSave();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("BatteryController state:");
+ pw.print(" mLevel="); pw.println(mLevel);
+ pw.print(" mPluggedIn="); pw.println(mPluggedIn);
+ pw.print(" mCharging="); pw.println(mCharging);
+ pw.print(" mCharged="); pw.println(mCharged);
+ pw.print(" mPowerSave="); pw.println(mPowerSave);
}
public void addStateChangedCallback(BatteryStateChangeCallback cb) {
@@ -75,9 +83,40 @@
mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
- for (BatteryStateChangeCallback cb : mChangeCallbacks) {
- cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
- }
+ fireBatteryLevelChanged();
+ } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
+ updatePowerSave();
}
}
+
+ public boolean isPowerSave() {
+ return mPowerSave;
+ }
+
+ private void updatePowerSave() {
+ final boolean powerSave = mPowerManager.isPowerSaveMode();
+ if (powerSave == mPowerSave) return;
+ mPowerSave = powerSave;
+ if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
+ firePowerSaveChanged();
+ }
+
+ private void fireBatteryLevelChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+ }
+ }
+
+ private void firePowerSaveChanged() {
+ final int N = mChangeCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mChangeCallbacks.get(i).onPowerSaveChanged();
+ }
+ }
+
+ public interface BatteryStateChangeCallback {
+ void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
+ void onPowerSaveChanged();
+ }
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index fc63490..56b8c92 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -6409,54 +6409,61 @@
mIsSystemRestore = isFullSystemRestore;
mFinished = false;
- if (filterSet == null) {
- // We want everything and a pony
- List<PackageInfo> apps
- = PackageManagerBackupAgent.getStorableApplications(mPackageManager);
- filterSet = packagesToNames(apps);
- if (DEBUG) {
- Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
- }
- }
-
- mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
-
- // Pro tem, we insist on moving the settings provider package to last place.
- // Keep track of whether it's in the list, and bump it down if so. We also
- // want to do the system package itself first if it's called for.
- boolean hasSystem = false;
- boolean hasSettings = false;
- for (int i = 0; i < filterSet.length; i++) {
- try {
- PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
- if ("android".equals(info.packageName)) {
- hasSystem = true;
- continue;
+ if (targetPackage != null) {
+ // Single package restore
+ mAcceptSet = new ArrayList<PackageInfo>();
+ mAcceptSet.add(targetPackage);
+ } else {
+ // Everything possible, or a target set
+ if (filterSet == null) {
+ // We want everything and a pony
+ List<PackageInfo> apps =
+ PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+ filterSet = packagesToNames(apps);
+ if (DEBUG) {
+ Slog.i(TAG, "Full restore; asking for " + filterSet.length + " apps");
}
- if (SETTINGS_PACKAGE.equals(info.packageName)) {
- hasSettings = true;
- continue;
- }
+ }
- if (appIsEligibleForBackup(info.applicationInfo)) {
- mAcceptSet.add(info);
+ mAcceptSet = new ArrayList<PackageInfo>(filterSet.length);
+
+ // Pro tem, we insist on moving the settings provider package to last place.
+ // Keep track of whether it's in the list, and bump it down if so. We also
+ // want to do the system package itself first if it's called for.
+ boolean hasSystem = false;
+ boolean hasSettings = false;
+ for (int i = 0; i < filterSet.length; i++) {
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(filterSet[i], 0);
+ if ("android".equals(info.packageName)) {
+ hasSystem = true;
+ continue;
+ }
+ if (SETTINGS_PACKAGE.equals(info.packageName)) {
+ hasSettings = true;
+ continue;
+ }
+
+ if (appIsEligibleForBackup(info.applicationInfo)) {
+ mAcceptSet.add(info);
+ }
+ } catch (NameNotFoundException e) {
+ // requested package name doesn't exist; ignore it
}
- } catch (NameNotFoundException e) {
- // requested package name doesn't exist; ignore it
}
- }
- if (hasSystem) {
- try {
- mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
- } catch (NameNotFoundException e) {
- // won't happen; we know a priori that it's valid
+ if (hasSystem) {
+ try {
+ mAcceptSet.add(0, mPackageManager.getPackageInfo("android", 0));
+ } catch (NameNotFoundException e) {
+ // won't happen; we know a priori that it's valid
+ }
}
- }
- if (hasSettings) {
- try {
- mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
- } catch (NameNotFoundException e) {
- // this one is always valid too
+ if (hasSettings) {
+ try {
+ mAcceptSet.add(mPackageManager.getPackageInfo(SETTINGS_PACKAGE, 0));
+ } catch (NameNotFoundException e) {
+ // this one is always valid too
+ }
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ca9f6eb..abee0c6 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -41,7 +41,6 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.app.AlarmManager;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -52,9 +51,7 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -224,10 +221,6 @@
AlarmManager mAlarmManager;
- // used in recursive route setting to add gateways for the host for which
- // a host route was requested.
- private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
-
private Tethering mTethering;
private KeyStore mKeyStore;
@@ -261,11 +254,6 @@
private CaptivePortalTracker mCaptivePortalTracker;
/**
- * The link properties that define the current links
- */
- private LinkProperties mCurrentLinkProperties[];
-
- /**
* A per Net list of the PID's that requested access to the net
* used both as a refcount and for per-PID DNS selection
*/
@@ -297,15 +285,6 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
- private static final boolean ADD = true;
- private static final boolean REMOVE = false;
-
- private static final boolean TO_DEFAULT_TABLE = true;
- private static final boolean TO_SECONDARY_TABLE = false;
-
- private static final boolean EXEMPT = true;
- private static final boolean UNEXEMPT = false;
-
/**
* used internally as a delayed event to make us switch back to the
* default network
@@ -450,19 +429,6 @@
private InetAddress mDefaultDns;
- // Lock for protecting access to mAddedRoutes and mExemptAddresses
- private final Object mRoutesLock = new Object();
-
- // this collection is used to refcount the added routes - if there are none left
- // it's time to remove the route from the route table
- @GuardedBy("mRoutesLock")
- private Collection<RouteInfo> mAddedRoutes = new ArrayList<RouteInfo>();
-
- // this collection corresponds to the entries of mAddedRoutes that have routing exemptions
- // used to handle cleanup of exempt rules
- @GuardedBy("mRoutesLock")
- private Collection<LinkAddress> mExemptAddresses = new ArrayList<LinkAddress>();
-
// used in DBG mode to track inet condition reports
private static final int INET_CONDITION_LOG_MAX_SIZE = 15;
private ArrayList mInetLog;
@@ -479,8 +445,6 @@
private SettingsObserver mSettingsObserver;
- private AppOpsManager mAppOpsManager;
-
private UserManager mUserManager;
NetworkConfig[] mNetConfigs;
@@ -514,8 +478,6 @@
// sequence number of NetworkRequests
private int mNextNetworkRequestId = 1;
- private static final int UID_UNUSED = -1;
-
/**
* Implements support for the legacy "one network per network type" model.
*
@@ -701,7 +663,6 @@
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
- mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1];
mRadioAttributes = new RadioAttributes[ConnectivityManager.MAX_RADIO_TYPE+1];
mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -884,8 +845,6 @@
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
mContext.registerReceiver(mProvisioningReceiver, filter);
- mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
-
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
@@ -1710,42 +1669,6 @@
}
/**
- * Check if the address falls into any of currently running VPN's route's.
- */
- private boolean isAddressUnderVpn(InetAddress address) {
- synchronized (mVpns) {
- synchronized (mRoutesLock) {
- int uid = UserHandle.getCallingUserId();
- Vpn vpn = mVpns.get(uid);
- if (vpn == null) {
- return false;
- }
-
- // Check if an exemption exists for this address.
- for (LinkAddress destination : mExemptAddresses) {
- if (!NetworkUtils.addressTypeMatches(address, destination.getAddress())) {
- continue;
- }
-
- int prefix = destination.getPrefixLength();
- InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
- InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
- prefix);
-
- if (addrMasked.equals(destMasked)) {
- return false;
- }
- }
-
- // Finally check if the address is covered by the VPN.
- return vpn.isAddressCovered(address);
- }
- }
- }
-
- /**
- * @deprecated use requestRouteToHostAddress instead
- *
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -1754,32 +1677,12 @@
* desired
* @return {@code true} on success, {@code false} on failure
*/
- public boolean requestRouteToHost(int networkType, int hostAddress, String packageName) {
- InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
-
- if (inetAddress == null) {
- return false;
- }
-
- return requestRouteToHostAddress(networkType, inetAddress.getAddress(), packageName);
- }
-
- /**
- * Ensure that a network route exists to deliver traffic to the specified
- * host via the specified network interface.
- * @param networkType the type of the network over which traffic to the
- * specified host is to be routed
- * @param hostAddress the IP address of the host to which the route is
- * desired
- * @return {@code true} on success, {@code false} on failure
- */
- public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress,
- String packageName) {
+ public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
}
- boolean exempt;
+
InetAddress addr;
try {
addr = InetAddress.getByAddress(hostAddress);
@@ -1787,26 +1690,6 @@
if (DBG) log("requestRouteToHostAddress got " + e.toString());
return false;
}
- // System apps may request routes bypassing the VPN to keep other networks working.
- if (Binder.getCallingUid() == Process.SYSTEM_UID) {
- exempt = true;
- } else {
- mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
- try {
- ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(packageName,
- 0);
- exempt = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- } catch (NameNotFoundException e) {
- throw new IllegalArgumentException("Failed to find calling package details", e);
- }
- }
-
- // Non-exempt routeToHost's can only be added if the host is not covered by the VPN.
- // This can be either because the VPN's routes do not cover the destination or a
- // system application added an exemption that covers this destination.
- if (!exempt && isAddressUnderVpn(addr)) {
- return false;
- }
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
@@ -1822,13 +1705,13 @@
}
return false;
}
+
DetailedState netState;
synchronized (nai) {
netState = nai.networkInfo.getDetailedState();
}
- if ((netState != DetailedState.CONNECTED &&
- netState != DetailedState.CAPTIVE_PORTAL_CHECK)) {
+ if (netState != DetailedState.CONNECTED && netState != DetailedState.CAPTIVE_PORTAL_CHECK) {
if (VDBG) {
log("requestRouteToHostAddress on down network "
+ "(" + networkType + ") - dropped"
@@ -1836,6 +1719,7 @@
}
return false;
}
+
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
@@ -1845,7 +1729,7 @@
lp = nai.linkProperties;
netId = nai.network.netId;
}
- boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId, uid);
+ boolean ok = addLegacyRouteToHost(lp, addr, netId, uid);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
} finally {
@@ -1853,17 +1737,7 @@
}
}
- private boolean addRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable,
- boolean exempt, int netId) {
- return modifyRoute(p, r, 0, ADD, toDefaultTable, exempt, netId, false, UID_UNUSED);
- }
-
- private boolean removeRoute(LinkProperties p, RouteInfo r, boolean toDefaultTable, int netId) {
- return modifyRoute(p, r, 0, REMOVE, toDefaultTable, UNEXEMPT, netId, false, UID_UNUSED);
- }
-
- private boolean modifyRouteToAddress(LinkProperties lp, InetAddress addr, boolean doAdd,
- boolean toDefaultTable, boolean exempt, int netId, int uid) {
+ private boolean addLegacyRouteToHost(LinkProperties lp, InetAddress addr, int netId, int uid) {
RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), addr);
if (bestRoute == null) {
bestRoute = RouteInfo.makeHostRoute(addr, lp.getInterfaceName());
@@ -1878,125 +1752,14 @@
bestRoute = RouteInfo.makeHostRoute(addr, bestRoute.getGateway(), iface);
}
}
- return modifyRoute(lp, bestRoute, 0, doAdd, toDefaultTable, exempt, netId, true, uid);
- }
-
- /*
- * TODO: Clean all this stuff up. Once we have UID-based routing, stuff will break due to
- * incorrect tracking of mAddedRoutes, so a cleanup becomes necessary and urgent. But at
- * the same time, there'll be no more need to track mAddedRoutes or mExemptAddresses,
- * or even have the concept of an exempt address, or do things like "selectBestRoute", or
- * determine "default" vs "secondary" table, etc., so the cleanup becomes possible.
- */
- private boolean modifyRoute(LinkProperties lp, RouteInfo r, int cycleCount, boolean doAdd,
- boolean toDefaultTable, boolean exempt, int netId, boolean legacy, int uid) {
- if ((lp == null) || (r == null)) {
- if (DBG) log("modifyRoute got unexpected null: " + lp + ", " + r);
+ if (VDBG) log("Adding " + bestRoute + " for interface " + bestRoute.getInterface());
+ try {
+ mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+ } catch (Exception e) {
+ // never crash - catch them all
+ if (DBG) loge("Exception trying to add a route: " + e);
return false;
}
-
- if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
- loge("Error modifying route - too much recursion");
- return false;
- }
-
- String ifaceName = r.getInterface();
- if(ifaceName == null) {
- loge("Error modifying route - no interface name");
- return false;
- }
- if (r.hasGateway()) {
- RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getAllRoutes(), r.getGateway());
- if (bestRoute != null) {
- if (bestRoute.getGateway().equals(r.getGateway())) {
- // if there is no better route, add the implied hostroute for our gateway
- bestRoute = RouteInfo.makeHostRoute(r.getGateway(), ifaceName);
- } else {
- // if we will connect to our gateway through another route, add a direct
- // route to it's gateway
- bestRoute = RouteInfo.makeHostRoute(r.getGateway(),
- bestRoute.getGateway(),
- ifaceName);
- }
- modifyRoute(lp, bestRoute, cycleCount+1, doAdd, toDefaultTable, exempt, netId,
- legacy, uid);
- }
- }
- if (doAdd) {
- if (VDBG) log("Adding " + r + " for interface " + ifaceName);
- try {
- if (toDefaultTable) {
- synchronized (mRoutesLock) {
- // only track default table - only one apps can effect
- mAddedRoutes.add(r);
- if (legacy) {
- mNetd.addLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.addRoute(netId, r);
- }
- if (exempt) {
- LinkAddress dest = r.getDestinationLinkAddress();
- if (!mExemptAddresses.contains(dest)) {
- mNetd.setHostExemption(dest);
- mExemptAddresses.add(dest);
- }
- }
- }
- } else {
- if (legacy) {
- mNetd.addLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.addRoute(netId, r);
- }
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (DBG) loge("Exception trying to add a route: " + e);
- return false;
- }
- } else {
- // if we remove this one and there are no more like it, then refcount==0 and
- // we can remove it from the table
- if (toDefaultTable) {
- synchronized (mRoutesLock) {
- mAddedRoutes.remove(r);
- if (mAddedRoutes.contains(r) == false) {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- if (legacy) {
- mNetd.removeLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.removeRoute(netId, r);
- }
- LinkAddress dest = r.getDestinationLinkAddress();
- if (mExemptAddresses.contains(dest)) {
- mNetd.clearHostExemption(dest);
- mExemptAddresses.remove(dest);
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
- return false;
- }
- } else {
- if (VDBG) log("not removing " + r + " as it's still in use");
- }
- }
- } else {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
- try {
- if (legacy) {
- mNetd.removeLegacyRouteForNetId(netId, r, uid);
- } else {
- mNetd.removeRoute(netId, r);
- }
- } catch (Exception e) {
- // never crash - catch them all
- if (VDBG) loge("Exception trying to remove a route: " + e);
- return false;
- }
- }
- }
return true;
}
@@ -2607,6 +2370,7 @@
*
* TODO - delete when we're sure all this functionallity is captured.
*/
+ /*
private void handleConnectivityChange(int netType, LinkProperties curLp, boolean doReset) {
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
boolean exempt = ConnectivityManager.isNetworkTypeExempt(netType);
@@ -2615,10 +2379,8 @@
+ " resetMask=" + resetMask);
}
- /*
- * If a non-default network is enabled, add the host routes that
- * will allow it's DNS servers to be accessed.
- */
+ // If a non-default network is enabled, add the host routes that
+ // will allow it's DNS servers to be accessed.
handleDnsConfigurationChange(netType);
LinkProperties newLp = null;
@@ -2719,7 +2481,7 @@
// TODO: Temporary notifying upstread change to Tethering.
// @see bug/4455071
- /** Notify TetheringService if interface name has been changed. */
+ // Notify TetheringService if interface name has been changed.
if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
if (isTetheringSupported()) {
@@ -2727,6 +2489,7 @@
}
}
}
+ */
/**
* Add and remove routes using the old properties (null if not previously connected),
@@ -2736,6 +2499,7 @@
* host routes should be set to the dns servers
* returns a boolean indicating the routes changed
*/
+ /*
private boolean updateRoutes(LinkProperties newLp, LinkProperties curLp,
boolean isLinkDefault, boolean exempt, int netId) {
Collection<RouteInfo> routesToAdd = null;
@@ -2789,6 +2553,7 @@
return routesChanged;
}
+ */
/**
* Reads the network specific MTU size from reources.
@@ -3264,10 +3029,12 @@
// any activity by applications trying to use this
// connection will fail until the provisioning network
// is enabled.
+ /*
for (RouteInfo r : lp.getRoutes()) {
removeRoute(lp, r, TO_DEFAULT_TABLE,
mNetTrackers[info.getType()].getNetwork().netId);
}
+ */
} else if (state == NetworkInfo.State.DISCONNECTED) {
} else if (state == NetworkInfo.State.SUSPENDED) {
} else if (state == NetworkInfo.State.CONNECTED) {
@@ -3283,8 +3050,10 @@
// TODO: Temporary allowing network configuration
// change not resetting sockets.
// @see bug/4455071
+ /*
handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()],
false);
+ */
break;
}
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
@@ -4683,7 +4452,7 @@
// Make a route to host so we check the specific interface.
if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
- hostAddr.getAddress(), null)) {
+ hostAddr.getAddress())) {
// Wait a short time to be sure the route is established ??
log("isMobileOk:"
+ " wait to establish route to hostAddr=" + hostAddr);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index f65a154..db423b0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -117,7 +117,7 @@
public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
class NetdResponseCode {
- /* Keep in sync with system/netd/ResponseCode.h */
+ /* Keep in sync with system/netd/server/ResponseCode.h */
public static final int InterfaceListResult = 110;
public static final int TetherInterfaceListResult = 111;
public static final int TetherDnsFwdTgtListResult = 112;
@@ -1745,36 +1745,6 @@
}
@Override
- public void setHostExemption(LinkAddress host) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "exempt", "add", host);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void clearHostExemption(LinkAddress host) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("interface", "fwmark", "exempt", "remove", host);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
- public void flushNetworkDnsCache(int netId) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("resolver", "flushnet", netId);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
- @Override
public void setFirewallEnabled(boolean enabled) {
enforceSystemUid();
try {
@@ -2019,18 +1989,9 @@
@Override
public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
- modifyLegacyRouteForNetId("add", netId, routeInfo, uid);
- }
-
- @Override
- public void removeLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
- modifyLegacyRouteForNetId("remove", netId, routeInfo, uid);
- }
-
- private void modifyLegacyRouteForNetId(String action, int netId, RouteInfo routeInfo, int uid) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final Command cmd = new Command("network", "route", "legacy", uid, action, netId);
+ final Command cmd = new Command("network", "route", "legacy", uid, "add", netId);
// create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
final LinkAddress la = routeInfo.getDestinationLinkAddress();
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index fe97c71..cf7e65c 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -603,7 +603,7 @@
return mUniqueId;
}
- /* These should be in sync with system/netd/mDnsResponseCode.h */
+ /* These should be in sync with system/netd/server/ResponseCode.h */
class NativeResponseCode {
public static final int SERVICE_DISCOVERY_FAILED = 602;
public static final int SERVICE_FOUND = 603;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 36d67ee..9a86136 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -77,6 +77,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -109,7 +110,7 @@
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 5;
+ private static final int DATABASE_VERSION = 6;
private final Context mContext;
@@ -130,6 +131,7 @@
private static final String ACCOUNTS_TYPE = "type";
private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
private static final String ACCOUNTS_PASSWORD = "password";
+ private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
private static final String TABLE_AUTHTOKENS = "authtokens";
private static final String AUTHTOKENS_ID = "_id";
@@ -196,6 +198,20 @@
/** protected by the {@link #cacheLock} */
private final HashMap<Account, HashMap<String, String>> authTokenCache =
new HashMap<Account, HashMap<String, String>>();
+ /**
+ * protected by the {@link #cacheLock}
+ *
+ * Caches the previous names associated with an account. Previous names
+ * should be cached because we expect that when an Account is renamed,
+ * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
+ * want to know if the accounts they care about have been renamed.
+ *
+ * The previous names are wrapped in an {@link AtomicReference} so that
+ * we can distinguish between those accounts with no previous names and
+ * those whose previous names haven't been cached (yet).
+ */
+ private final HashMap<Account, AtomicReference<String>> previousNameCache =
+ new HashMap<Account, AtomicReference<String>>();
UserAccounts(Context context, int userId) {
this.userId = userId;
@@ -517,6 +533,57 @@
}
@Override
+ public String getPreviousName(Account account) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getPreviousName: " + account
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (account == null) throw new IllegalArgumentException("account is null");
+ UserAccounts accounts = getUserAccountsForCaller();
+ long identityToken = clearCallingIdentity();
+ try {
+ return readPreviousNameInternal(accounts, account);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private String readPreviousNameInternal(UserAccounts accounts, Account account) {
+ if (account == null) {
+ return null;
+ }
+ synchronized (accounts.cacheLock) {
+ AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
+ if (previousNameRef == null) {
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ Cursor cursor = db.query(
+ TABLE_ACCOUNTS,
+ new String[]{ ACCOUNTS_PREVIOUS_NAME },
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[] { account.name, account.type },
+ null,
+ null,
+ null);
+ try {
+ if (cursor.moveToNext()) {
+ String previousName = cursor.getString(0);
+ previousNameRef = new AtomicReference<String>(previousName);
+ accounts.previousNameCache.put(account, previousNameRef);
+ return previousName;
+ } else {
+ return null;
+ }
+ } finally {
+ cursor.close();
+ }
+ } else {
+ return previousNameRef.get();
+ }
+ }
+ }
+
+ @Override
public String getUserData(Account account, String key) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getUserData: " + account
@@ -859,6 +926,119 @@
}
@Override
+ public void renameAccount(
+ IAccountManagerResponse response, Account accountToRename, String newName) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
+ + ", caller's uid " + Binder.getCallingUid()
+ + ", pid " + Binder.getCallingPid());
+ }
+ if (accountToRename == null) throw new IllegalArgumentException("account is null");
+ checkAuthenticateAccountsPermission(accountToRename);
+ UserAccounts accounts = getUserAccountsForCaller();
+ long identityToken = clearCallingIdentity();
+ try {
+ Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
+ Bundle result = new Bundle();
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
+ try {
+ response.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, e.getMessage());
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private Account renameAccountInternal(
+ UserAccounts accounts, Account accountToRename, String newName) {
+ Account resultAccount = null;
+ /*
+ * Cancel existing notifications. Let authenticators
+ * re-post notifications as required. But we don't know if
+ * the authenticators have bound their notifications to
+ * now stale account name data.
+ *
+ * With a rename api, we might not need to do this anymore but it
+ * shouldn't hurt.
+ */
+ cancelNotification(
+ getSigninRequiredNotificationId(accounts, accountToRename),
+ new UserHandle(accounts.userId));
+ synchronized(accounts.credentialsPermissionNotificationIds) {
+ for (Pair<Pair<Account, String>, Integer> pair:
+ accounts.credentialsPermissionNotificationIds.keySet()) {
+ if (accountToRename.equals(pair.first.first)) {
+ int id = accounts.credentialsPermissionNotificationIds.get(pair);
+ cancelNotification(id, new UserHandle(accounts.userId));
+ }
+ }
+ }
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ db.beginTransaction();
+ boolean isSuccessful = false;
+ Account renamedAccount = new Account(newName, accountToRename.type);
+ try {
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
+ values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
+ final long accountId = getAccountIdLocked(db, accountToRename);
+ if (accountId >= 0) {
+ final String[] argsAccountId = { String.valueOf(accountId) };
+ db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
+ db.setTransactionSuccessful();
+ isSuccessful = true;
+ }
+ } finally {
+ db.endTransaction();
+ if (isSuccessful) {
+ /*
+ * Database transaction was successful. Clean up cached
+ * data associated with the account in the user profile.
+ */
+ insertAccountIntoCacheLocked(accounts, renamedAccount);
+ /*
+ * Extract the data and token caches before removing the
+ * old account to preserve the user data associated with
+ * the account.
+ */
+ HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
+ HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
+ removeAccountFromCacheLocked(accounts, accountToRename);
+ /*
+ * Update the cached data associated with the renamed
+ * account.
+ */
+ accounts.userDataCache.put(renamedAccount, tmpData);
+ accounts.authTokenCache.put(renamedAccount, tmpTokens);
+ accounts.previousNameCache.put(
+ renamedAccount,
+ new AtomicReference<String>(accountToRename.name));
+ resultAccount = renamedAccount;
+
+ if (accounts.userId == UserHandle.USER_OWNER) {
+ /*
+ * Owner's account was renamed, rename the account for
+ * those users with which the account was shared.
+ */
+ List<UserInfo> users = mUserManager.getUsers(true);
+ for (UserInfo user : users) {
+ if (!user.isPrimary() && user.isRestricted()) {
+ renameSharedAccountAsUser(accountToRename, newName, user.id);
+ }
+ }
+ }
+ sendAccountsChangedBroadcast(accounts.userId);
+ }
+ }
+ }
+ return resultAccount;
+ }
+
+ @Override
public void removeAccount(IAccountManagerResponse response, Account account) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccount: " + account
@@ -2061,6 +2241,26 @@
}
@Override
+ public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
+ userId = handleIncomingUser(userId);
+ UserAccounts accounts = getUserAccounts(userId);
+ SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, newName);
+ values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
+ int r = db.update(
+ TABLE_SHARED_ACCOUNTS,
+ values,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[] { account.name, account.type });
+ if (r > 0) {
+ // Recursively rename the account.
+ renameAccountInternal(accounts, account, newName);
+ }
+ return r > 0;
+ }
+
+ @Override
public boolean removeSharedAccountAsUser(Account account, int userId) {
userId = handleIncomingUser(userId);
UserAccounts accounts = getUserAccounts(userId);
@@ -2557,6 +2757,7 @@
+ ACCOUNTS_NAME + " TEXT NOT NULL, "
+ ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ ACCOUNTS_PASSWORD + " TEXT, "
+ + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
@@ -2592,6 +2793,10 @@
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
}
+ private void addOldAccountNameColumn(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
+ }
+
private void createAccountsDeletionTrigger(SQLiteDatabase db) {
db.execSQL(""
+ " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
@@ -2642,6 +2847,11 @@
oldVersion++;
}
+ if (oldVersion == 5) {
+ addOldAccountNameColumn(db);
+ oldVersion++;
+ }
+
if (oldVersion != newVersion) {
Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
}
@@ -3050,6 +3260,7 @@
}
accounts.userDataCache.remove(account);
accounts.authTokenCache.remove(account);
+ accounts.previousNameCache.remove(account);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 01a2fc2..d948942 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -724,14 +724,6 @@
return retVal;
}
- //TODO: Temporary handling upstream change triggered without
- // CONNECTIVITY_ACTION. Only to accomodate interface
- // switch during HO.
- // @see bug/4455071
- public void handleTetherIfaceChange() {
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
- }
-
class TetherInterfaceSM extends StateMachine {
// notification from the master SM that it's not in tether mode
static final int CMD_TETHER_MODE_DEAD = 1;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d15254b..7b68d55 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -488,18 +488,6 @@
return tun;
}
- /**
- * Check if a given address is covered by the VPN's routing rules.
- */
- public boolean isAddressCovered(InetAddress address) {
- synchronized (Vpn.this) {
- if (!isRunningLocked()) {
- return false;
- }
- return RouteInfo.selectBestRoute(mConfig.routes, address) != null;
- }
- }
-
private boolean isRunningLocked() {
return mVpnUsers != null;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a2dd15e..b0ccd62 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -776,8 +776,8 @@
if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0) {
if (DEBUG) {
Log.d(TAG, "No active session to adjust, skipping media only volume event");
- return;
}
+ return;
}
try {
mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8e625c5..a896550 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -90,9 +90,6 @@
"com.android.mms",
"com.android.example.notificationshowcase"
));
- private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
- "com.google.android.deskclock"
- ));
private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList(
"android",
"com.android.systemui"
@@ -313,7 +310,8 @@
}
private boolean isAlarm(NotificationRecord record) {
- return ALARM_PACKAGES.contains(record.sbn.getPackageName());
+ return record.isCategory(Notification.CATEGORY_ALARM)
+ || record.isCategory(Notification.CATEGORY_EVENT);
}
private boolean isCall(NotificationRecord record) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 29ef9c1..eb90085 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5675,13 +5675,28 @@
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
+ final DisplayContent displayContent = getDisplayContentLocked(displayId);
+ if (displayContent == null) {
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ + ": returning null. No Display for displayId=" + displayId);
+ return null;
+ }
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ int dw = displayInfo.logicalWidth;
+ int dh = displayInfo.logicalHeight;
+ if (dw == 0 || dh == 0) {
+ if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ + ": returning null. logical widthxheight=" + dw + "x" + dh);
+ return null;
+ }
+
Bitmap rawss = null;
int maxLayer = 0;
final Rect frame = new Rect();
+ final Rect stackBounds = new Rect();
float scale = 0;
- int dw, dh;
int rot = Surface.ROTATION_0;
boolean screenshotReady;
@@ -5697,7 +5712,15 @@
int retryCount = 0;
WindowState appWin = null;
- do {
+ final boolean appIsImTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+
+ final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
+ * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+
+ while (true) {
if (retryCount++ > 0) {
try {
Thread.sleep(100);
@@ -5705,28 +5728,9 @@
}
}
synchronized(mWindowMap) {
- final DisplayContent displayContent = getDisplayContentLocked(displayId);
- if (displayContent == null) {
- return null;
- }
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- dw = displayInfo.logicalWidth;
- dh = displayInfo.logicalHeight;
-
- int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
- * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
- aboveAppLayer += TYPE_LAYER_MULTIPLIER;
-
- boolean isImeTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
-
// Figure out the part of the screen that is actually the app.
- boolean including = false;
appWin = null;
final WindowList windows = displayContent.getWindowList();
- final Rect stackBounds = new Rect();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState ws = windows.get(i);
if (!ws.mHasSurface) {
@@ -5735,27 +5739,22 @@
if (ws.mLayer >= aboveAppLayer) {
continue;
}
- // When we will skip windows: when we are not including
- // ones behind a window we didn't skip, and we are actually
- // taking a screenshot of a specific app.
- if (!including && appToken != null) {
- // Also, we can possibly skip this window if it is not
- // an IME target or the application for the screenshot
- // is not the current IME target.
- if (!ws.mIsImWindow || !isImeTarget) {
- // And finally, this window is of no interest if it
- // is not associated with the screenshot app.
- if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
- continue;
- }
- appWin = ws;
- ws.getStackBounds(stackBounds);
+ if (ws.mIsImWindow) {
+ if (!appIsImTarget) {
+ continue;
}
+ } else if (ws.mIsWallpaper) {
+ // Fall through.
+ } else if (appToken != null) {
+ if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+ // This app window is of no interest if it is not associated with the
+ // screenshot app.
+ continue;
+ }
+ appWin = ws;
}
- // We keep on including windows until we go past a full-screen
- // window.
- including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
+ // Include this window.
final WindowStateAnimator winAnim = ws.mWinAnimator;
if (maxLayer < winAnim.mSurfaceLayer) {
@@ -5774,6 +5773,7 @@
int right = wf.right - cr.right;
int bottom = wf.bottom - cr.bottom;
frame.union(left, top, right, bottom);
+ ws.getStackBounds(stackBounds);
frame.intersect(stackBounds);
}
@@ -5789,60 +5789,49 @@
"Screenshot: Couldn't find a surface matching " + appToken);
return null;
}
+
if (!screenshotReady) {
+ if (retryCount > MAX_SCREENSHOT_RETRIES) {
+ Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken +
+ " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
+ appWin.mWinAnimator.mDrawState)));
+ return null;
+ }
+
// Delay and hope that window gets drawn.
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken
+ ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
continue;
}
- // Constrain frame to the screen size.
- frame.intersect(0, 0, dw, dh);
+ // Screenshot is ready to be taken. Everything from here below will continue
+ // through the bottom of the loop and return a value. We only stay in the loop
+ // because we don't want to release the mWindowMap lock until the screenshot is
+ // taken.
- if (frame.isEmpty() || maxLayer == 0) {
+ if (maxLayer == 0) {
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
- + ": returning null frame=" + frame.toShortString() + " maxLayer="
- + maxLayer);
+ + ": returning null maxLayer=" + maxLayer);
return null;
}
- // The screenshot API does not apply the current screen rotation.
- rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
- int fw = frame.width();
- int fh = frame.height();
-
+ // Constrain frame to the screen size.
+ frame.intersect(0, 0, dw, dh);
// Constrain thumbnail to smaller of screen width or height. Assumes aspect
// of thumbnail is the same as the screen (in landscape) or square.
- scale = Math.max(width / (float) fw, height / (float) fh);
- /*
- float targetWidthScale = width / (float) fw;
- float targetHeightScale = height / (float) fh;
- if (fw <= fh) {
- scale = targetWidthScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
- scale = targetHeightScale;
- }
- } else {
- scale = targetHeightScale;
- // If aspect of thumbnail is the same as the screen (in landscape),
- // select the slightly larger value so we fill the entire bitmap
- if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
- scale = targetWidthScale;
- }
- }
- */
+ scale = Math.max(width / (float) frame.width(), height / (float) frame.height());
+ dw = (int)(dw * scale);
+ dh = (int)(dh * scale);
- // The screen shot will contain the entire screen.
- dw = (int)(dw*scale);
- dh = (int)(dh*scale);
+ // The screenshot API does not apply the current screen rotation.
+ rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
- int tmp = dw;
+ final int tmp = dw;
dw = dh;
dh = tmp;
rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
}
+
if (DEBUG_SCREENSHOT) {
Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+ maxLayer + " appToken=" + appToken);
@@ -5853,27 +5842,31 @@
+ " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
}
}
- // TODO: Replace 'false' in the following line with a variable that indicates
- // whether the screenshot should use the identity transformation matrix
- // (e.g., enable it when taking a screenshot for recents, since we might be in
- // the middle of the rotation animation, but don't want a rotated recent image).
- // TODO: Replace 'new Rect()' with the portion of the screen to capture for the
- // screenshot.
- rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer, false);
- }
- } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
- if (retryCount > MAX_SCREENSHOT_RETRIES) Slog.i(TAG, "Screenshot max retries " +
- retryCount + " of " + appToken + " appWin=" + (appWin == null ?
- "null" : (appWin + " drawState=" + appWin.mWinAnimator.mDrawState)));
- if (rawss == null) {
- Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
- + ") to layer " + maxLayer);
- return null;
+ ScreenRotationAnimation screenRotationAnimation =
+ mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+ final boolean inRotation = screenRotationAnimation != null &&
+ screenRotationAnimation.isAnimating();
+ if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG,
+ "Taking screenshot while rotating");
+
+ rawss = SurfaceControl.screenshot(new Rect(), dw, dh, minLayer, maxLayer,
+ inRotation);
+ if (rawss == null) {
+ Slog.w(TAG, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+ + ") to layer " + maxLayer);
+ return null;
+ }
+ }
+
+ break;
}
- Bitmap bm = Bitmap.createBitmap(width, height, force565 ? Config.RGB_565 : rawss.getConfig());
- bm.eraseColor(0xFF000000);
+ Bitmap bm = Bitmap.createBitmap(width, height, force565 ?
+ Config.RGB_565 : rawss.getConfig());
+ if (DEBUG_SCREENSHOT) {
+ bm.eraseColor(0xFF000000);
+ }
frame.scale(scale);
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 88aaafc..9e3dec8 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -162,12 +162,10 @@
mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
nextConnBroadcast.get();
- // verify that both routes were added and DNS was flushed
+ // verify that both routes were added
int mobileNetId = mMobile.tracker.getNetwork().netId;
verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
verify(mNetManager).addRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
- verify(mNetManager).flushNetworkDnsCache(mobileNetId);
-
}
public void testMobileWifiHandoff() throws Exception {
@@ -204,7 +202,6 @@
int wifiNetId = mWifi.tracker.getNetwork().netId;
verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V4));
verify(mNetManager).addRoute(eq(wifiNetId), eq(WIFI_ROUTE_V6));
- verify(mNetManager).flushNetworkDnsCache(wifiNetId);
verify(mMobile.tracker).teardown();
int mobileNetId = mMobile.tracker.getNetwork().netId;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 50be1dc..1e0d6de 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -100,29 +100,32 @@
}
public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
- SQLiteDatabase db = getWritableDatabase();
- ContentValues values = new ContentValues();
- // Generate a random ID for the model.
- values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
- values.put(SoundModelContract.KEY_DATA, soundModel.data);
- values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
-
- boolean status = true;
- if (db.insertWithOnConflict(
- SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
- for (Keyphrase keyphrase : soundModel.keyphrases) {
- status &= addOrUpdateKeyphrase(db, soundModel.uuid, keyphrase);
+ synchronized(this) {
+ SQLiteDatabase db = getWritableDatabase();
+ ContentValues values = new ContentValues();
+ // Generate a random ID for the model.
+ values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
+ values.put(SoundModelContract.KEY_DATA, soundModel.data);
+ values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+
+ boolean status = true;
+ if (db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
+ SQLiteDatabase.CONFLICT_REPLACE) != -1) {
+ for (Keyphrase keyphrase : soundModel.keyphrases) {
+ status &= addOrUpdateKeyphraseLocked(db, soundModel.uuid, keyphrase);
+ }
+ db.close();
+ return status;
+ } else {
+ Slog.w(TAG, "Failed to persist sound model to database");
+ db.close();
+ return false;
}
- db.close();
- return status;
- } else {
- Slog.w(TAG, "Failed to persist sound model to database");
- db.close();
- return false;
}
}
- private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
+ private boolean addOrUpdateKeyphraseLocked(
+ SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
ContentValues values = new ContentValues();
values.put(KeyphraseContract.KEY_ID, keyphrase.id);
values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
@@ -143,62 +146,66 @@
* Deletes the sound model and associated keyphrases.
*/
public boolean deleteKeyphraseSoundModel(UUID uuid) {
- SQLiteDatabase db = getWritableDatabase();
- String modelId = uuid.toString();
- String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
- boolean status = true;
- if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
- Slog.w(TAG, "No sound models deleted from the database");
- status = false;
+ synchronized(this) {
+ SQLiteDatabase db = getWritableDatabase();
+ String modelId = uuid.toString();
+ String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
+ boolean status = true;
+ if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
+ Slog.w(TAG, "No sound models deleted from the database");
+ status = false;
+ }
+ String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
+ if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
+ Slog.w(TAG, "No keyphrases deleted from the database");
+ status = false;
+ }
+ db.close();
+ return status;
}
- String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
- if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
- Slog.w(TAG, "No keyphrases deleted from the database");
- status = false;
- }
- db.close();
- return status;
}
/**
* Lists all the keyphrase sound models currently registered with the system.
*/
public List<KeyphraseSoundModel> getKephraseSoundModels() {
- List<KeyphraseSoundModel> models = new ArrayList<>();
- String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
- SQLiteDatabase db = getReadableDatabase();
- Cursor c = db.rawQuery(selectQuery, null);
-
- // looping through all rows and adding to list
- if (c.moveToFirst()) {
- do {
- int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
- if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
- // Ignore non-keyphrase sound models.
- continue;
- }
- String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
- byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
- // Get all the keyphrases for this this sound model.
- // Validate the sound model.
- if (id == null) {
- Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
- continue;
- }
- KeyphraseSoundModel model = new KeyphraseSoundModel(
- UUID.fromString(id), data, getKeyphrasesForSoundModel(db, id));
- if (DBG) {
- Slog.d(TAG, "Adding model: " + model);
- }
- models.add(model);
- } while (c.moveToNext());
+ synchronized(this) {
+ List<KeyphraseSoundModel> models = new ArrayList<>();
+ String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+ SQLiteDatabase db = getReadableDatabase();
+ Cursor c = db.rawQuery(selectQuery, null);
+
+ // looping through all rows and adding to list
+ if (c.moveToFirst()) {
+ do {
+ int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+ if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+ // Ignore non-keyphrase sound models.
+ continue;
+ }
+ String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
+ byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+ // Get all the keyphrases for this this sound model.
+ // Validate the sound model.
+ if (id == null) {
+ Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
+ continue;
+ }
+ KeyphraseSoundModel model = new KeyphraseSoundModel(
+ UUID.fromString(id), data, getKeyphrasesForSoundModelLocked(db, id));
+ if (DBG) {
+ Slog.d(TAG, "Adding model: " + model);
+ }
+ models.add(model);
+ } while (c.moveToNext());
+ }
+ c.close();
+ db.close();
+ return models;
}
- c.close();
- db.close();
- return models;
}
- private Keyphrase[] getKeyphrasesForSoundModel(SQLiteDatabase db, String modelId) {
+ private Keyphrase[] getKeyphrasesForSoundModelLocked(SQLiteDatabase db, String modelId) {
List<Keyphrase> keyphrases = new ArrayList<>();
String selectQuery = "SELECT * FROM " + KeyphraseContract.TABLE
+ " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
@@ -243,7 +250,7 @@
}
- private String getCommaSeparatedString(int[] users) {
+ private static String getCommaSeparatedString(int[] users) {
if (users == null || users.length == 0) {
return "";
}
@@ -255,7 +262,7 @@
return csv.substring(0, csv.length() - 1);
}
- private int[] getArrayForCommaSeparatedString(String text) {
+ private static int[] getArrayForCommaSeparatedString(String text) {
if (TextUtils.isEmpty(text)) {
return null;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7b2e4f1..5d9e107 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -266,13 +266,13 @@
+ Manifest.permission.MANAGE_VOICE_KEYPHRASES);
}
}
+ }
- final long caller = Binder.clearCallingIdentity();
- try {
- return mDbHelper.getKephraseSoundModels();
- } finally {
- Binder.restoreCallingIdentity(caller);
- }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ return mDbHelper.getKephraseSoundModels();
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}
@@ -287,29 +287,31 @@
if (model == null) {
throw new IllegalArgumentException("Model must not be null");
}
+ }
- final long caller = Binder.clearCallingIdentity();
- try {
- boolean success = false;
- if (model.keyphrases == null) {
- // If the keyphrases are not present in the model, delete the model.
- success = mDbHelper.deleteKeyphraseSoundModel(model.uuid);
- } else {
- // Else update the model.
- success = mDbHelper.addOrUpdateKeyphraseSoundModel(model);
- }
- if (success) {
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ boolean success = false;
+ if (model.keyphrases == null) {
+ // If the keyphrases are not present in the model, delete the model.
+ success = mDbHelper.deleteKeyphraseSoundModel(model.uuid);
+ } else {
+ // Else update the model.
+ success = mDbHelper.addOrUpdateKeyphraseSoundModel(model);
+ }
+ if (success) {
+ synchronized (this) {
// Notify the voice interaction service of a change in sound models.
if (mImpl != null && mImpl.mService != null) {
mImpl.notifySoundModelsChangedLocked();
}
- return SoundTriggerHelper.STATUS_OK;
- } else {
- return SoundTriggerHelper.STATUS_ERROR;
}
- } finally {
- Binder.restoreCallingIdentity(caller);
+ return SoundTriggerHelper.STATUS_OK;
+ } else {
+ return SoundTriggerHelper.STATUS_ERROR;
}
+ } finally {
+ Binder.restoreCallingIdentity(caller);
}
}
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoClient.java b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
index 8024831..08d1391 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoClient.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoClient.java
@@ -26,17 +26,20 @@
/**
* Remote class to invoke callbacks in InCallUI related to supporting video in calls.
*/
-public class RemoteCallVideoClient implements IBinder.DeathRecipient {
+public class RemoteCallVideoClient {
private final ICallVideoClient mCallVideoClient;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+ }
+ };
+
+ /** {@hide} */
RemoteCallVideoClient(ICallVideoClient callVideoProvider) throws RemoteException {
mCallVideoClient = callVideoProvider;
- mCallVideoClient.asBinder().linkToDeath(this, 0);
- }
-
- @Override
- public void binderDied() {
- mCallVideoClient.asBinder().unlinkToDeath(this, 0);
+ mCallVideoClient.asBinder().linkToDeath(mDeathRecipient, 0);
}
/**
@@ -49,9 +52,11 @@
*
* @param videoCallProfile The requested video call profile.
*/
- public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile)
- throws RemoteException {
- mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+ public void receiveSessionModifyRequest(VideoCallProfile videoCallProfile) {
+ try {
+ mCallVideoClient.receiveSessionModifyRequest(videoCallProfile);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -66,9 +71,13 @@
* @param requestedProfile The original request which was sent to the remote device.
* @param responseProfile The actual profile changes made by the remote device.
*/
- public void receiveSessionModifyResponse(int status, VideoCallProfile requestedProfile,
- VideoCallProfile responseProfile) throws RemoteException {
- mCallVideoClient.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ public void receiveSessionModifyResponse(
+ int status, VideoCallProfile requestedProfile, VideoCallProfile responseProfile) {
+ try {
+ mCallVideoClient.receiveSessionModifyResponse(
+ status, requestedProfile, responseProfile);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -81,8 +90,11 @@
*
* @param event The event.
*/
- public void handleCallSessionEvent(int event) throws RemoteException {
- mCallVideoClient.handleCallSessionEvent(event);
+ public void handleCallSessionEvent(int event) {
+ try {
+ mCallVideoClient.handleCallSessionEvent(event);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -92,8 +104,11 @@
* @param width The updated peer video width.
* @param height The updated peer video height.
*/
- public void updatePeerDimensions(int width, int height) throws RemoteException {
- mCallVideoClient.updatePeerDimensions(width, height);
+ public void updatePeerDimensions(int width, int height) {
+ try {
+ mCallVideoClient.updatePeerDimensions(width, height);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -101,8 +116,11 @@
*
* @param dataUsage The updated data usage.
*/
- public void updateCallDataUsage(int dataUsage) throws RemoteException {
- mCallVideoClient.updateCallDataUsage(dataUsage);
+ public void updateCallDataUsage(int dataUsage) {
+ try {
+ mCallVideoClient.updateCallDataUsage(dataUsage);
+ } catch (RemoteException e) {
+ }
}
/**
@@ -110,8 +128,10 @@
*
* @param callCameraCapabilities The changed camera capabilities.
*/
- public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities)
- throws RemoteException {
- mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+ public void handleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities) {
+ try {
+ mCallVideoClient.handleCameraCapabilitiesChange(callCameraCapabilities);
+ } catch (RemoteException e) {
+ }
}
-}
+}
\ No newline at end of file
diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
index 9ca156f..e5216b5 100644
--- a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
+++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java
@@ -31,6 +31,7 @@
import android.media.MediaPlayer.OnPreparedListener;
import android.media.browse.MediaBrowserItem;
import android.media.browse.MediaBrowserService;
+import android.media.browse.MediaBrowserService.BrowserRoot;
import android.media.session.MediaSession;
import android.net.Uri;
import android.net.wifi.WifiManager;
@@ -115,24 +116,27 @@
}
@Override
- public Uri onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
- return BROWSE_URI;
+ protected BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ return new BrowserRoot(BROWSE_URI, null);
}
@Override
- public List<MediaBrowserItem> onLoadChildren(Uri parentUri) {
+ protected List<MediaBrowserItem> onLoadChildren(Uri parentUri) {
final ArrayList<MediaBrowserItem> results = new ArrayList();
for (int i=0; i<10; i++) {
- results.add(new MediaBrowserItem.Builder(Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
- MediaBrowserItem.FLAG_BROWSABLE, "Title " + i).setSummary("Summary " + i).build());
+ results.add(new MediaBrowserItem.Builder(
+ Uri.withAppendedPath(BASE_URI, Integer.toString(i)),
+ MediaBrowserItem.FLAG_BROWSABLE, "Title " + i)
+ .setSummary("Summary " + i)
+ .build());
}
return results;
}
@Override
- public Bitmap onGetThumbnail(Uri uri, int width, int height, int density) {
+ protected Bitmap onGetThumbnail(Uri uri, int width, int height) {
return null;
}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index e750bb6..cc710f9 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -30,6 +30,12 @@
private final Callback mHotwordCallback = new Callback() {
@Override
+ public void onAvailabilityChanged(int status) {
+ Log.i(TAG, "onAvailabilityChanged(" + status + ")");
+ hotwordAvailabilityChangeHelper(status);
+ }
+
+ @Override
public void onDetected(byte[] data) {
Log.i(TAG, "onDetected");
}
@@ -51,17 +57,6 @@
+ Arrays.toString(getKeyphraseEnrollmentInfo().listKeyphraseMetadata()));
mHotwordDetector = createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
- testHotwordAvailabilityStates();
- }
-
- @Override
- public void onSoundModelsChanged() {
- int availability = mHotwordDetector.getAvailability();
- Log.i(TAG, "Hotword availability = " + availability);
- if (availability == AlwaysOnHotwordDetector.STATE_INVALID) {
- mHotwordDetector = createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
- }
- testHotwordAvailabilityStates();
}
@Override
@@ -73,12 +68,13 @@
return START_NOT_STICKY;
}
- private void testHotwordAvailabilityStates() {
- int availability = mHotwordDetector.getAvailability();
+ private void hotwordAvailabilityChangeHelper(int availability) {
Log.i(TAG, "Hotword availability = " + availability);
switch (availability) {
case AlwaysOnHotwordDetector.STATE_INVALID:
Log.i(TAG, "STATE_INVALID");
+ mHotwordDetector =
+ createAlwaysOnHotwordDetector("Hello There", "en-US", mHotwordCallback);
break;
case AlwaysOnHotwordDetector.STATE_HARDWARE_UNAVAILABLE:
Log.i(TAG, "STATE_HARDWARE_UNAVAILABLE");