diff options
113 files changed, 3323 insertions, 1798 deletions
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java index 2673031ddbaf..45037262a824 100644 --- a/cmds/bu/src/com/android/commands/bu/Backup.java +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -20,6 +20,7 @@ import android.app.backup.IBackupManager; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.system.OsConstants; import android.util.Log; import java.io.IOException; @@ -50,13 +51,11 @@ public final class Backup { return; } - int socketFd = Integer.parseInt(nextArg()); - String arg = nextArg(); if (arg.equals("backup")) { - doFullBackup(socketFd); + doFullBackup(OsConstants.STDOUT_FILENO); } else if (arg.equals("restore")) { - doFullRestore(socketFd); + doFullRestore(OsConstants.STDIN_FILENO); } else { Log.e(TAG, "Invalid operation '" + arg + "'"); } diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index b7c2c2229400..47047b86a6ec 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -39,6 +39,7 @@ import android.content.pm.VerificationParams; import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.IUserManager; import android.os.Process; @@ -57,7 +58,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; - import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; @@ -823,6 +823,7 @@ public final class Pm { byte[] tag = null; String originatingUriString = null; String referrer = null; + String abi = null; while ((opt=nextOption()) != null) { if (opt.equals("-l")) { @@ -893,12 +894,34 @@ public final class Pm { System.err.println("Error: must supply argument for --referrer"); return; } + } else if (opt.equals("--abi")) { + abi = nextOptionData(); + if (abi == null) { + System.err.println("Error: must supply argument for --abi"); + return; + } } else { System.err.println("Error: Unknown option: " + opt); return; } } + if (abi != null) { + final String[] supportedAbis = Build.SUPPORTED_ABIS; + boolean matched = false; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + matched = true; + break; + } + } + + if (!matched) { + System.err.println("Error: abi " + abi + " not supported on this device."); + return; + } + } + final ContainerEncryptionParams encryptionParams; if (algo != null || iv != null || key != null || macAlgo != null || macKey != null || tag != null) { @@ -976,8 +999,9 @@ public final class Pm { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); - mPm.installPackageWithVerificationAndEncryptionEtc(apkURI, null, obs, installFlags, - installerPackageName, verificationParams, encryptionParams); + mPm.installPackageWithVerificationEncryptionAndAbiOverrideEtc(apkURI, null, + obs, installFlags, installerPackageName, verificationParams, + encryptionParams, abi); synchronized (obs) { while (!obs.finished) { diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index f332c9df2c11..85e970c25b84 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -43,7 +43,7 @@ import java.util.ArrayList; * {@link Request} subclass describing the type of operation to perform -- currently the * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}. * - * <p>Once a request is submitted, the voice system will process it and evetually deliver + * <p>Once a request is submitted, the voice system will process it and eventually deliver * the result to the request object. The application can cancel a pending request at any * time. * @@ -51,7 +51,7 @@ import java.util.ArrayList; * if an activity is being restarted with retained state, it will retain the current * VoiceInteractor and any outstanding requests. Because of this, you should always use * {@link Request#getActivity() Request.getActivity} to get back to the activity of a - * request, rather than holding on to the actvitity instance yourself, either explicitly + * request, rather than holding on to the activity instance yourself, either explicitly * or implicitly through a non-static inner class. */ public class VoiceInteractor { @@ -236,7 +236,7 @@ public class VoiceInteractor { * Reports that the current interaction can not be complete with voice, so the * application will need to switch to a traditional input UI. Applications should * only use this when they need to completely bail out of the voice interaction - * and switch to a traditional UI. When the resonsponse comes back, the voice + * and switch to a traditional UI. When the response comes back, the voice * system has handled the request and is ready to switch; at that point the application * can start a new non-voice activity. Be sure when starting the new activity * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index da5cb10b664e..46f082eec104 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -22,7 +22,6 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import com.android.internal.backup.BackupConstants; import com.android.internal.backup.IBackupTransport; /** @@ -32,6 +31,13 @@ import com.android.internal.backup.IBackupTransport; * @hide */ public class BackupTransport { + public static final int TRANSPORT_OK = 0; + public static final int TRANSPORT_ERROR = 1; + public static final int TRANSPORT_NOT_INITIALIZED = 2; + public static final int TRANSPORT_PACKAGE_REJECTED = 3; + public static final int AGENT_ERROR = 4; + public static final int AGENT_UNKNOWN = 5; + IBackupTransport mBinderImpl = new TransportImpl(); /** @hide */ public IBinder getBinder() { @@ -99,10 +105,50 @@ public class BackupTransport { } // ------------------------------------------------------------------------------------ + // Device-level operations common to both key/value and full-data storage + + /** + * Initialize the server side storage for this device, erasing all stored data. + * The transport may send the request immediately, or may buffer it. After + * this is called, {@link #finishBackup} will be called to ensure the request + * is sent and received successfully. + * + * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far) or + * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure). + */ + public int initializeDevice() { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Erase the given application's data from the backup destination. This clears + * out the given package's data from the current backup set, making it as though + * the app had never yet been backed up. After this is called, {@link finishBackup} + * must be called to ensure that the operation is recorded successfully. + * + * @return the same error codes as {@link #performBackup}. + */ + public int clearBackupData(PackageInfo packageInfo) { + return BackupTransport.TRANSPORT_ERROR; + } + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup}, {@link #performFullBackup}, or {@link clearBackupData} + * to ensure that all data is sent and the operation properly finalized. Only when this + * method returns true can a backup be assumed to have succeeded. + * + * @return the same error codes as {@link #performBackup} or {@link #performFullBackup}. + */ + public int finishBackup() { + return BackupTransport.TRANSPORT_ERROR; + } + + // ------------------------------------------------------------------------------------ // Key/value incremental backup support interfaces /** - * Verify that this is a suitable time for a backup pass. This should return zero + * Verify that this is a suitable time for a key/value backup pass. This should return zero * if a backup is reasonable right now, some positive value otherwise. This method * will be called outside of the {@link #performBackup}/{@link #finishBackup} pair. * @@ -117,19 +163,6 @@ public class BackupTransport { } /** - * Initialize the server side storage for this device, erasing all stored data. - * The transport may send the request immediately, or may buffer it. After - * this is called, {@link #finishBackup} will be called to ensure the request - * is sent and received successfully. - * - * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far) or - * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure). - */ - public int initializeDevice() { - return BackupConstants.TRANSPORT_ERROR; - } - - /** * Send one application's key/value data update to the backup destination. The * transport may send the data immediately, or may buffer it. After this is called, * {@link #finishBackup} will be called to ensure the data is sent and recorded successfully. @@ -143,37 +176,13 @@ public class BackupTransport { * must be erased prior to the storage of the data provided here. The purpose of this * is to provide a guarantee that no stale data exists in the restore set when the * device begins providing incremental backups. - * @return one of {@link BackupConstants#TRANSPORT_OK} (OK so far), - * {@link BackupConstants#TRANSPORT_ERROR} (on network error or other failure), or - * {@link BackupConstants#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has + * @return one of {@link BackupTransport#TRANSPORT_OK} (OK so far), + * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure), or + * {@link BackupTransport#TRANSPORT_NOT_INITIALIZED} (if the backend dataset has * become lost due to inactivity purge or some other reason and needs re-initializing) */ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd) { - return BackupConstants.TRANSPORT_ERROR; - } - - /** - * Erase the give application's data from the backup destination. This clears - * out the given package's data from the current backup set, making it as though - * the app had never yet been backed up. After this is called, {@link finishBackup} - * must be called to ensure that the operation is recorded successfully. - * - * @return the same error codes as {@link #performBackup}. - */ - public int clearBackupData(PackageInfo packageInfo) { - return BackupConstants.TRANSPORT_ERROR; - } - - /** - * Finish sending application data to the backup destination. This must be - * called after {@link #performBackup} or {@link clearBackupData} to ensure that - * all data is sent. Only when this method returns true can a backup be assumed - * to have succeeded. - * - * @return the same error codes as {@link #performBackup}. - */ - public int finishBackup() { - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } // ------------------------------------------------------------------------------------ @@ -210,12 +219,12 @@ public class BackupTransport { * or {@link #getCurrentRestoreSet}. * @param packages List of applications to restore (if data is available). * Application data will be restored in the order given. - * @return One of {@link BackupConstants#TRANSPORT_OK} (OK so far, call - * {@link #nextRestorePackage}) or {@link BackupConstants#TRANSPORT_ERROR} + * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far, call + * {@link #nextRestorePackage}) or {@link BackupTransport#TRANSPORT_ERROR} * (an error occurred, the restore should be aborted and rescheduled). */ public int startRestore(long token, PackageInfo[] packages) { - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } /** @@ -235,7 +244,7 @@ public class BackupTransport { * @return the same error codes as {@link #startRestore}. */ public int getRestoreData(ParcelFileDescriptor outFd) { - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } /** @@ -247,6 +256,78 @@ public class BackupTransport { "Transport finishRestore() not implemented"); } + // ------------------------------------------------------------------------------------ + // Full backup interfaces + + /** + * Verify that this is a suitable time for a full-data backup pass. This should return zero + * if a backup is reasonable right now, some positive value otherwise. This method + * will be called outside of the {@link #performFullBackup}/{@link #finishBackup} pair. + * + * <p>If this is not a suitable time for a backup, the transport should return a + * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. + * + * @see #requestBackupTime() + */ + public long requestFullBackupTime() { + return 0; + } + + /** + * Begin the process of sending an application's full-data archive to the backend. + * The description of the package whose data will be delivered is provided, as well as + * the socket file descriptor on which the transport will receive the data itself. + * + * <p>If the package is not eligible for backup, the transport should return + * {@link BackupTransport#TRANSPORT_PACKAGE_REJECTED}. In this case the system will + * simply proceed with the next candidate if any, or finish the full backup operation + * if all apps have been processed. + * + * <p>After the transport returns {@link BackupTransport#TRANSPORT_OK} from this + * method, the OS will proceed to call {@link #sendBackupData()} one or more times + * to deliver the application's data as a streamed tarball. The transport should not + * read() from the socket except as instructed to via the {@link #sendBackupData(int)} + * method. + * + * <p>After all data has been delivered to the transport, the system will call + * {@link #finishBackup()}. At this point the transport should commit the data to + * its datastore, if appropriate, and close the socket that had been provided in + * {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}. + * + * @param targetPackage The package whose data is to follow. + * @param socket The socket file descriptor through which the data will be provided. + * If the transport returns {@link #TRANSPORT_PACKAGE_REJECTED} here, it must still + * close this file descriptor now; otherwise it should be cached for use during + * succeeding calls to {@link #sendBackupData(int)}, and closed in response to + * {@link #finishBackup()}. + * @return TRANSPORT_PACKAGE_REJECTED to indicate that the stated application is not + * to be backed up; TRANSPORT_OK to indicate that the OS may proceed with delivering + * backup data; TRANSPORT_ERROR to indicate a fatal error condition that precludes + * performing a backup at this time. + */ + public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) { + return BackupTransport.TRANSPORT_PACKAGE_REJECTED; + } + + /** + * Tells the transport to read {@code numBytes} bytes of data from the socket file + * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)} + * call, and deliver those bytes to the datastore. + * + * @param numBytes The number of bytes of tarball data available to be read from the + * socket. + * @return TRANSPORT_OK on successful processing of the data; TRANSPORT_ERROR to + * indicate a fatal error situation. If an error is returned, the system will + * call finishBackup() and stop attempting backups until after a backoff and retry + * interval. + */ + public int sendBackupData(int numBytes) { + return BackupTransport.TRANSPORT_ERROR; + } + /** * Bridge between the actual IBackupTransport implementation and the stable API. If the * binder interface needs to change, we use this layer to translate so that we can diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 44a6a5d12a5d..70668e11d5d6 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -433,6 +433,13 @@ interface IPackageManager { in VerificationParams verificationParams, in ContainerEncryptionParams encryptionParams); + void installPackageWithVerificationEncryptionAndAbiOverrideEtc(in Uri packageURI, + in IPackageInstallObserver observer, in IPackageInstallObserver2 observer2, + int flags, in String installerPackageName, + in VerificationParams verificationParams, + in ContainerEncryptionParams encryptionParams, + in String packageAbiOverride); + int installExistingPackageAsUser(String packageName, int userId); void verifyPendingInstall(int id, int verificationCode); diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index a71a74dbef5a..723eda1fd4a6 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -194,6 +194,8 @@ public final class HdmiCec { DEVICE_RECORDER, // ADDR_RECORDER_3 DEVICE_TUNER, // ADDR_TUNER_4 DEVICE_PLAYBACK, // ADDR_PLAYBACK_3 + DEVICE_RESERVED, + DEVICE_RESERVED, DEVICE_TV, // ADDR_SPECIFIC_USE }; @@ -210,6 +212,8 @@ public final class HdmiCec { "Recorder_3", "Tuner_4", "Playback_3", + "Reserved_1", + "Reserved_2", "Secondary_TV", }; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 2f2aba328661..a48a38882085 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -40,6 +40,7 @@ import android.util.ArrayMap; import android.util.Log; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.Protocol; import java.net.InetAddress; @@ -807,11 +808,34 @@ public class ConnectivityManager { * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. */ public int startUsingNetworkFeature(int networkType, String feature) { - try { - return mService.startUsingNetworkFeature(networkType, feature, - new Binder()); - } catch (RemoteException e) { - return -1; + NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); + if (netCap == null) { + Log.d(TAG, "Can't satisfy startUsingNetworkFeature for " + networkType + ", " + + feature); + return PhoneConstants.APN_REQUEST_FAILED; + } + + NetworkRequest request = null; + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l != null) { + Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest); + renewRequestLocked(l); + if (l.currentNetwork != null) { + return PhoneConstants.APN_ALREADY_ACTIVE; + } else { + return PhoneConstants.APN_REQUEST_STARTED; + } + } + + request = requestNetworkForFeatureLocked(netCap); + } + if (request != null) { + Log.d(TAG, "starting startUsingNeworkFeature for request " + request); + return PhoneConstants.APN_REQUEST_STARTED; + } else { + Log.d(TAG, " request Failed"); + return PhoneConstants.APN_REQUEST_FAILED; } } @@ -831,11 +855,172 @@ public class ConnectivityManager { * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. */ public int stopUsingNetworkFeature(int networkType, String feature) { - try { - return mService.stopUsingNetworkFeature(networkType, feature); - } catch (RemoteException e) { + NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature); + if (netCap == null) { + Log.d(TAG, "Can't satisfy stopUsingNetworkFeature for " + networkType + ", " + + feature); return -1; } + + NetworkRequest request = removeRequestForFeature(netCap); + if (request != null) { + Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature); + releaseNetworkRequest(request); + } + return 1; + } + + private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) { + if (networkType == TYPE_MOBILE) { + int cap = -1; + if ("enableMMS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_MMS; + } else if ("enableSUPL".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_SUPL; + } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_DUN; + } else if ("enableHIPRI".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_INTERNET; + } else if ("enableFOTA".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_FOTA; + } else if ("enableIMS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_IMS; + } else if ("enableCBS".equals(feature)) { + cap = NetworkCapabilities.NET_CAPABILITY_CBS; + } else { + return null; + } + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + netCap.addNetworkCapability(cap); + return netCap; + } else if (networkType == TYPE_WIFI) { + if ("p2p".equals(feature)) { + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); + return netCap; + } + } + return null; + } + + private int legacyTypeForNetworkCapabilities(NetworkCapabilities netCap) { + if (netCap == null) return TYPE_NONE; + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) { + return TYPE_MOBILE_CBS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { + return TYPE_MOBILE_IMS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) { + return TYPE_MOBILE_FOTA; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { + return TYPE_MOBILE_DUN; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { + return TYPE_MOBILE_SUPL; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { + return TYPE_MOBILE_MMS; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + return TYPE_MOBILE_HIPRI; + } + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P)) { + return TYPE_WIFI_P2P; + } + return TYPE_NONE; + } + + private static class LegacyRequest { + NetworkCapabilities networkCapabilities; + NetworkRequest networkRequest; + int expireSequenceNumber; + Network currentNetwork; + int delay = -1; + NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() { + @Override + public void onAvailable(NetworkRequest request, Network network) { + currentNetwork = network; + Log.d(TAG, "startUsingNetworkFeature got Network:" + network); + network.bindProcessForHostResolution(); + } + @Override + public void onLost(NetworkRequest request, Network network) { + if (network.equals(currentNetwork)) { + currentNetwork = null; + network.unbindProcessForHostResolution(); + } + Log.d(TAG, "startUsingNetworkFeature lost Network:" + network); + } + }; + } + + private HashMap<NetworkCapabilities, LegacyRequest> sLegacyRequests = + new HashMap<NetworkCapabilities, LegacyRequest>(); + + private NetworkRequest findRequestForFeature(NetworkCapabilities netCap) { + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l != null) return l.networkRequest; + } + return null; + } + + private void renewRequestLocked(LegacyRequest l) { + l.expireSequenceNumber++; + Log.d(TAG, "renewing request to seqNum " + l.expireSequenceNumber); + sendExpireMsgForFeature(l.networkCapabilities, l.expireSequenceNumber, l.delay); + } + + private void expireRequest(NetworkCapabilities netCap, int sequenceNum) { + int ourSeqNum = -1; + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.get(netCap); + if (l == null) return; + ourSeqNum = l.expireSequenceNumber; + if (l.expireSequenceNumber == sequenceNum) { + releaseNetworkRequest(l.networkRequest); + sLegacyRequests.remove(netCap); + } + } + Log.d(TAG, "expireRequest with " + ourSeqNum + ", " + sequenceNum); + } + + private NetworkRequest requestNetworkForFeatureLocked(NetworkCapabilities netCap) { + int delay = -1; + int type = legacyTypeForNetworkCapabilities(netCap); + try { + delay = mService.getRestoreDefaultNetworkDelay(type); + } catch (RemoteException e) {} + LegacyRequest l = new LegacyRequest(); + l.networkCapabilities = netCap; + l.delay = delay; + l.expireSequenceNumber = 0; + l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0, + REQUEST, type); + if (l.networkRequest == null) return null; + sLegacyRequests.put(netCap, l); + sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay); + return l.networkRequest; + } + + private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) { + if (delay >= 0) { + Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay); + Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); + sCallbackHandler.sendMessageDelayed(msg, delay); + } + } + + private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) { + synchronized (sLegacyRequests) { + LegacyRequest l = sLegacyRequests.remove(netCap); + if (l == null) return null; + return l.networkRequest; + } } /** @@ -1782,8 +1967,10 @@ public class ConnectivityManager { public static final int CALLBACK_RELEASED = BASE + 8; /** @hide */ public static final int CALLBACK_EXIT = BASE + 9; + /** @hide obj = NetworkCapabilities, arg1 = seq number */ + private static final int EXPIRE_LEGACY_REQUEST = BASE + 10; - private static class CallbackHandler extends Handler { + private class CallbackHandler extends Handler { private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap; private final AtomicInteger mRefCount; private static final String TAG = "ConnectivityManager.CallbackHandler"; @@ -1903,6 +2090,10 @@ public class ConnectivityManager { getLooper().quit(); break; } + case EXPIRE_LEGACY_REQUEST: { + expireRequest((NetworkCapabilities)message.obj, message.arg1); + break; + } } } @@ -1954,8 +2145,9 @@ public class ConnectivityManager { private final static int LISTEN = 1; private final static int REQUEST = 2; - private NetworkRequest somethingForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec, int action) { + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, + NetworkCallbackListener networkCallbackListener, int timeoutSec, int action, + int legacyType) { NetworkRequest networkRequest = null; if (networkCallbackListener == null) { throw new IllegalArgumentException("null NetworkCallbackListener"); @@ -1968,7 +2160,7 @@ public class ConnectivityManager { new Binder()); } else { networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), - timeoutSec, new Binder()); + timeoutSec, new Binder(), legacyType); } if (networkRequest != null) { synchronized(sNetworkCallbackListener) { @@ -1998,7 +2190,7 @@ public class ConnectivityManager { */ public NetworkRequest requestNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener) { - return somethingForNetwork(need, networkCallbackListener, 0, REQUEST); + return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE); } /** @@ -2021,7 +2213,8 @@ public class ConnectivityManager { */ public NetworkRequest requestNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener, int timeoutSec) { - return somethingForNetwork(need, networkCallbackListener, timeoutSec, REQUEST); + return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST, + TYPE_NONE); } /** @@ -2099,7 +2292,7 @@ public class ConnectivityManager { */ public NetworkRequest listenForNetwork(NetworkCapabilities need, NetworkCallbackListener networkCallbackListener) { - return somethingForNetwork(need, networkCallbackListener, 0, LISTEN); + return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE); } /** diff --git a/core/java/android/net/ConnectivityServiceProtocol.java b/core/java/android/net/ConnectivityServiceProtocol.java deleted file mode 100644 index 74096b407b0b..000000000000 --- a/core/java/android/net/ConnectivityServiceProtocol.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import static com.android.internal.util.Protocol.BASE_CONNECTIVITY_SERVICE; - -/** - * Describes the Internal protocols used to communicate with ConnectivityService. - * @hide - */ -public class ConnectivityServiceProtocol { - - private static final int BASE = BASE_CONNECTIVITY_SERVICE; - - private ConnectivityServiceProtocol() {} - - /** - * This is a contract between ConnectivityService and various bearers. - * A NetworkFactory is an abstract entity that creates NetworkAgent objects. - * The bearers register with ConnectivityService using - * ConnectivityManager.registerNetworkFactory, where they pass in a Messenger - * to be used to deliver the following Messages. - */ - public static class NetworkFactoryProtocol { - private NetworkFactoryProtocol() {} - /** - * Pass a network request to the bearer. If the bearer believes it can - * satisfy the request it should connect to the network and create a - * NetworkAgent. Once the NetworkAgent is fully functional it will - * register itself with ConnectivityService using registerNetworkAgent. - * If the bearer cannot immediately satisfy the request (no network, - * user disabled the radio, lower-scored network) it should remember - * any NetworkRequests it may be able to satisfy in the future. It may - * disregard any that it will never be able to service, for example - * those requiring a different bearer. - * msg.obj = NetworkRequest - * msg.arg1 = score - the score of the any network currently satisfying this - * request. If this bearer knows in advance it cannot - * exceed this score it should not try to connect, holding the request - * for the future. - * Note that subsequent events may give a different (lower - * or higher) score for this request, transmitted to each - * NetworkFactory through additional CMD_REQUEST_NETWORK msgs - * with the same NetworkRequest but an updated score. - * Also, network conditions may change for this bearer - * allowing for a better score in the future. - */ - public static final int CMD_REQUEST_NETWORK = BASE; - - /** - * Cancel a network request - * msg.obj = NetworkRequest - */ - public static final int CMD_CANCEL_REQUEST = BASE + 1; - } -} diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index baec36ad48d3..5f1ff3e1d9d6 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -158,7 +158,7 @@ interface IConnectivityManager in NetworkCapabilities nc, int score); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, int timeoutSec, in IBinder binder); + in Messenger messenger, int timeoutSec, in IBinder binder, int legacy); NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, in PendingIntent operation); @@ -170,4 +170,6 @@ interface IConnectivityManager in PendingIntent operation); void releaseNetworkRequest(in NetworkRequest networkRequest); + + int getRestoreDefaultNetworkDelay(int networkType); } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 1c18ba564009..7e8b1f1acafd 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -24,85 +24,39 @@ import android.os.Messenger; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; -import android.util.SparseArray; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; /** - * A Utility class for handling NetworkRequests. - * - * Created by bearer-specific code to handle tracking requests, scores, - * network data and handle communicating with ConnectivityService. Two - * abstract methods: connect and disconnect are used to act on the - * underlying bearer code. Connect is called when we have a NetworkRequest - * and our score is better than the current handling network's score, while - * disconnect is used when ConnectivityService requests a disconnect. + * A Utility class for handling for communicating between bearer-specific + * code and ConnectivityService. * * A bearer may have more than one NetworkAgent if it can simultaneously * support separate networks (IMS / Internet / MMS Apns on cellular, or - * perhaps connections with different SSID or P2P for Wi-Fi). The bearer - * code should pass its NetworkAgents the NetworkRequests each NetworkAgent - * can handle, demultiplexing for different network types. The bearer code - * can also filter out requests it can never handle. + * perhaps connections with different SSID or P2P for Wi-Fi). * - * Each NetworkAgent needs to be given a score and NetworkCapabilities for - * their potential network. While disconnected, the NetworkAgent will check - * each time its score changes or a NetworkRequest changes to see if - * the NetworkAgent can provide a higher scored network for a NetworkRequest - * that the NetworkAgent's NetworkCapabilties can satisfy. This condition will - * trigger a connect request via connect(). After connection, connection data - * should be given to the NetworkAgent by the bearer, including LinkProperties - * NetworkCapabilties and NetworkInfo. After that the NetworkAgent will register - * with ConnectivityService and forward the data on. * @hide */ public abstract class NetworkAgent extends Handler { - private final SparseArray<NetworkRequestAndScore> mNetworkRequests = new SparseArray<>(); - private boolean mConnectionRequested = false; - - private AsyncChannel mAsyncChannel; + private volatile AsyncChannel mAsyncChannel; private final String LOG_TAG; private static final boolean DBG = true; private static final boolean VDBG = true; - // TODO - this class shouldn't cache data or it runs the risk of getting out of sync - // Make the API require each of these when any is updated so we have the data we need, - // without caching. - private LinkProperties mLinkProperties; - private NetworkInfo mNetworkInfo; - private NetworkCapabilities mNetworkCapabilities; - private int mNetworkScore; - private boolean mRegistered = false; private final Context mContext; - private AtomicBoolean mHasRequests = new AtomicBoolean(false); - - // TODO - add a name member for logging purposes. - - protected final Object mLockObj = new Object(); - + private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>(); private static final int BASE = Protocol.BASE_NETWORK_AGENT; /** - * Sent by self to queue up a new/modified request. - * obj = NetworkRequestAndScore - */ - private static final int CMD_ADD_REQUEST = BASE + 1; - - /** - * Sent by self to queue up the removal of a request. - * obj = NetworkRequest - */ - private static final int CMD_REMOVE_REQUEST = BASE + 2; - - /** * Sent by ConnectivityService to the NetworkAgent to inform it of * suspected connectivity problems on its network. The NetworkAgent * should take steps to verify and correct connectivity. */ - public static final int CMD_SUSPECT_BAD = BASE + 3; + public static final int CMD_SUSPECT_BAD = BASE; /** * Sent by the NetworkAgent (note the EVENT vs CMD prefix) to @@ -110,84 +64,63 @@ public abstract class NetworkAgent extends Handler { * Sent when the NetworkInfo changes, mainly due to change of state. * obj = NetworkInfo */ - public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 4; + public static final int EVENT_NETWORK_INFO_CHANGED = BASE + 1; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkCapabilties. * obj = NetworkCapabilities */ - public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 5; + public static final int EVENT_NETWORK_CAPABILITIES_CHANGED = BASE + 2; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * NetworkProperties. * obj = NetworkProperties */ - public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 6; + public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3; /** * Sent by the NetworkAgent to ConnectivityService to pass the current * network score. - * arg1 = network score int + * obj = network score Integer */ - public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 7; + public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; - public NetworkAgent(Looper looper, Context context, String logTag) { + public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, + NetworkCapabilities nc, LinkProperties lp, int score) { super(looper); LOG_TAG = logTag; mContext = context; - } - - /** - * When conditions are right, register with ConnectivityService. - * Connditions include having a well defined network and a request - * that justifies it. The NetworkAgent will remain registered until - * disconnected. - * TODO - this should have all data passed in rather than caching - */ - private void registerSelf() { - synchronized(mLockObj) { - if (!mRegistered && mConnectionRequested && - mNetworkInfo != null && mNetworkInfo.isConnected() && - mNetworkCapabilities != null && - mLinkProperties != null && - mNetworkScore != 0) { - if (DBG) log("Registering NetworkAgent"); - mRegistered = true; - ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(mNetworkInfo), - new LinkProperties(mLinkProperties), - new NetworkCapabilities(mNetworkCapabilities), mNetworkScore); - } else if (DBG && !mRegistered) { - String err = "Not registering due to "; - if (mConnectionRequested == false) err += "no Connect requested "; - if (mNetworkInfo == null) err += "null NetworkInfo "; - if (mNetworkInfo != null && mNetworkInfo.isConnected() == false) { - err += "NetworkInfo disconnected "; - } - if (mLinkProperties == null) err += "null LinkProperties "; - if (mNetworkCapabilities == null) err += "null NetworkCapabilities "; - if (mNetworkScore == 0) err += "null NetworkScore"; - log(err); - } + if (ni == null || nc == null || lp == null) { + throw new IllegalArgumentException(); } + + if (DBG) log("Registering NetworkAgent"); + ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni), + new LinkProperties(lp), new NetworkCapabilities(nc), score); } @Override public void handleMessage(Message msg) { switch (msg.what) { case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { - synchronized (mLockObj) { - if (mAsyncChannel != null) { - log("Received new connection while already connected!"); - } else { - if (DBG) log("NetworkAgent fully connected"); - mAsyncChannel = new AsyncChannel(); - mAsyncChannel.connected(null, this, msg.replyTo); - mAsyncChannel.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, - AsyncChannel.STATUS_SUCCESSFUL); + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + } else { + if (DBG) log("NetworkAgent fully connected"); + AsyncChannel ac = new AsyncChannel(); + ac.connected(null, this, msg.replyTo); + ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannel.STATUS_SUCCESSFUL); + synchronized (mPreConnectedQueue) { + mAsyncChannel = ac; + for (Message m : mPreConnectedQueue) { + ac.sendMessage(m); + } + mPreConnectedQueue.clear(); } } break; @@ -199,213 +132,69 @@ public abstract class NetworkAgent extends Handler { } case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { if (DBG) log("NetworkAgent channel lost"); - disconnect(); - clear(); + // let the client know CS is done with us. + unwanted(); + synchronized (mPreConnectedQueue) { + mAsyncChannel = null; + } break; } case CMD_SUSPECT_BAD: { log("Unhandled Message " + msg); break; } - case CMD_ADD_REQUEST: { - handleAddRequest(msg); - break; - } - case CMD_REMOVE_REQUEST: { - handleRemoveRequest(msg); - break; - } - } - } - - private void clear() { - synchronized(mLockObj) { - mNetworkRequests.clear(); - mHasRequests.set(false); - mConnectionRequested = false; - mAsyncChannel = null; - mRegistered = false; - } - } - - private static class NetworkRequestAndScore { - NetworkRequest req; - int score; - - NetworkRequestAndScore(NetworkRequest networkRequest, int score) { - req = networkRequest; - this.score = score; } } - private void handleAddRequest(Message msg) { - NetworkRequestAndScore n = (NetworkRequestAndScore)msg.obj; - // replaces old request, updating score - mNetworkRequests.put(n.req.requestId, n); - mHasRequests.set(true); - evalScores(); - } - - private void handleRemoveRequest(Message msg) { - NetworkRequest networkRequest = (NetworkRequest)msg.obj; - - if (mNetworkRequests.get(networkRequest.requestId) != null) { - mNetworkRequests.remove(networkRequest.requestId); - if (mNetworkRequests.size() == 0) mHasRequests.set(false); - evalScores(); - } - } - - /** - * Called to go through our list of requests and see if we're - * good enough to try connecting, or if we have gotten worse and - * need to disconnect. - * - * Once we are registered, does nothing: we disconnect when requested via - * CMD_CHANNEL_DISCONNECTED, generated by either a loss of connection - * between modules (bearer or ConnectivityService dies) or more commonly - * when the NetworkInfo reports to ConnectivityService it is disconnected. - */ - private void evalScores() { - synchronized(mLockObj) { - if (mRegistered) { - if (VDBG) log("evalScores - already connected - size=" + mNetworkRequests.size()); - // already trying - return; - } - if (VDBG) log("evalScores!"); - for (int i=0; i < mNetworkRequests.size(); i++) { - int score = mNetworkRequests.valueAt(i).score; - if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore); - if (score < mNetworkScore) { - // have a request that has a lower scored network servicing it - // (or no network) than we could provide, so let's connect! - mConnectionRequested = true; - connect(); - return; - } - } - // Our score is not high enough to satisfy any current request. - // This can happen if our score goes down after a connection is - // requested but before we actually connect. In this case, disconnect - // rather than continue trying - there's no point connecting if we know - // we'll just be torn down as soon as we do. - if (mConnectionRequested) { - mConnectionRequested = false; - disconnect(); + private void queueOrSendMessage(int what, Object obj) { + synchronized (mPreConnectedQueue) { + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(what, obj); + } else { + Message msg = Message.obtain(); + msg.what = what; + msg.obj = obj; + mPreConnectedQueue.add(msg); } } } - public void addNetworkRequest(NetworkRequest networkRequest, int score) { - if (DBG) log("adding NetworkRequest " + networkRequest + " with score " + score); - sendMessage(obtainMessage(CMD_ADD_REQUEST, - new NetworkRequestAndScore(networkRequest, score))); - } - - public void removeNetworkRequest(NetworkRequest networkRequest) { - if (DBG) log("removing NetworkRequest " + networkRequest); - sendMessage(obtainMessage(CMD_REMOVE_REQUEST, networkRequest)); - } - /** * Called by the bearer code when it has new LinkProperties data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). */ public void sendLinkProperties(LinkProperties linkProperties) { - linkProperties = new LinkProperties(linkProperties); - synchronized(mLockObj) { - mLinkProperties = linkProperties; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, linkProperties); - } else { - registerSelf(); - } - } + queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties)); } /** * Called by the bearer code when it has new NetworkInfo data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). */ public void sendNetworkInfo(NetworkInfo networkInfo) { - networkInfo = new NetworkInfo(networkInfo); - synchronized(mLockObj) { - mNetworkInfo = networkInfo; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_INFO_CHANGED, networkInfo); - } else { - registerSelf(); - } - } + queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo)); } /** * Called by the bearer code when it has new NetworkCapabilities data. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy in anticipation of registering. This call - * may also prompt registration if it causes the NetworkAgent to meet - * the conditions (fully configured, connected, satisfys a request and - * has sufficient score). - * Note that if these capabilities make the network non-useful, - * ConnectivityServce will tear this network down. */ public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) { - networkCapabilities = new NetworkCapabilities(networkCapabilities); - synchronized(mLockObj) { - mNetworkCapabilities = networkCapabilities; - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, networkCapabilities); - } else { - registerSelf(); - } - } - } - - public NetworkCapabilities getNetworkCapabilities() { - synchronized(mLockObj) { - return new NetworkCapabilities(mNetworkCapabilities); - } + queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, + new NetworkCapabilities(networkCapabilities)); } /** * Called by the bearer code when it has a new score for this network. - * If we're a registered NetworkAgent, this new data will get forwarded on, - * otherwise we store a copy. */ - public synchronized void sendNetworkScore(int score) { - synchronized(mLockObj) { - mNetworkScore = score; - evalScores(); - if (mAsyncChannel != null) { - mAsyncChannel.sendMessage(EVENT_NETWORK_SCORE_CHANGED, mNetworkScore); - } else { - registerSelf(); - } - } - } - - public boolean hasRequests() { - return mHasRequests.get(); + public void sendNetworkScore(int score) { + queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score)); } - public boolean isConnectionRequested() { - synchronized(mLockObj) { - return mConnectionRequested; - } - } - - - abstract protected void connect(); - abstract protected void disconnect(); + /** + * Called when ConnectivityService has indicated they no longer want this network. + * The parent factory should (previously) have received indication of the change + * as well, either canceling NetworkRequests or altering their score such that this + * network won't be immediately requested again. + */ + abstract protected void unwanted(); protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java new file mode 100644 index 000000000000..a20e8e76eaf1 --- /dev/null +++ b/core/java/android/net/NetworkFactory.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; + +/** + * A NetworkFactory is an entity that creates NetworkAgent objects. + * The bearers register with ConnectivityService using {@link #register} and + * their factory will start receiving scored NetworkRequests. NetworkRequests + * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by + * overridden function. All of these can be dynamic - changing NetworkCapabilities + * or score forces re-evaluation of all current requests. + * + * If any requests pass the filter some overrideable functions will be called. + * If the bearer only cares about very simple start/stopNetwork callbacks, those + * functions can be overridden. If the bearer needs more interaction, it can + * override addNetworkRequest and removeNetworkRequest which will give it each + * request that passes their current filters. + * @hide + **/ +public class NetworkFactory extends Handler { + private static final boolean DBG = true; + + private static final int BASE = Protocol.BASE_NETWORK_FACTORY; + /** + * Pass a network request to the bearer. If the bearer believes it can + * satisfy the request it should connect to the network and create a + * NetworkAgent. Once the NetworkAgent is fully functional it will + * register itself with ConnectivityService using registerNetworkAgent. + * If the bearer cannot immediately satisfy the request (no network, + * user disabled the radio, lower-scored network) it should remember + * any NetworkRequests it may be able to satisfy in the future. It may + * disregard any that it will never be able to service, for example + * those requiring a different bearer. + * msg.obj = NetworkRequest + * msg.arg1 = score - the score of the any network currently satisfying this + * request. If this bearer knows in advance it cannot + * exceed this score it should not try to connect, holding the request + * for the future. + * Note that subsequent events may give a different (lower + * or higher) score for this request, transmitted to each + * NetworkFactory through additional CMD_REQUEST_NETWORK msgs + * with the same NetworkRequest but an updated score. + * Also, network conditions may change for this bearer + * allowing for a better score in the future. + */ + public static final int CMD_REQUEST_NETWORK = BASE; + + /** + * Cancel a network request + * msg.obj = NetworkRequest + */ + public static final int CMD_CANCEL_REQUEST = BASE + 1; + + /** + * Internally used to set our best-guess score. + * msg.arg1 = new score + */ + private static final int CMD_SET_SCORE = BASE + 2; + + /** + * Internally used to set our current filter for coarse bandwidth changes with + * technology changes. + * msg.obj = new filter + */ + private static final int CMD_SET_FILTER = BASE + 3; + + private final Context mContext; + private final String LOG_TAG; + + private final SparseArray<NetworkRequestInfo> mNetworkRequests = + new SparseArray<NetworkRequestInfo>(); + + private int mScore; + private NetworkCapabilities mCapabilityFilter; + + private int mRefCount = 0; + private Messenger mMessenger = null; + + public NetworkFactory(Looper looper, Context context, String logTag, + NetworkCapabilities filter) { + super(looper); + LOG_TAG = logTag; + mContext = context; + mCapabilityFilter = filter; + } + + public void register() { + if (DBG) log("Registering NetworkFactory"); + if (mMessenger == null) { + mMessenger = new Messenger(this); + ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG); + } + } + + public void unregister() { + if (DBG) log("Unregistering NetworkFactory"); + if (mMessenger != null) { + ConnectivityManager.from(mContext).unregisterNetworkFactory(mMessenger); + mMessenger = null; + } + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CMD_REQUEST_NETWORK: { + handleAddRequest((NetworkRequest)msg.obj, msg.arg1); + break; + } + case CMD_CANCEL_REQUEST: { + handleRemoveRequest((NetworkRequest) msg.obj); + break; + } + case CMD_SET_SCORE: { + handleSetScore(msg.arg1); + break; + } + case CMD_SET_FILTER: { + handleSetFilter((NetworkCapabilities) msg.obj); + break; + } + } + } + + private class NetworkRequestInfo { + public final NetworkRequest request; + public int score; + public boolean requested; // do we have a request outstanding, limited by score + + public NetworkRequestInfo(NetworkRequest request, int score) { + this.request = request; + this.score = score; + this.requested = false; + } + } + + private void handleAddRequest(NetworkRequest request, int score) { + NetworkRequestInfo n = mNetworkRequests.get(request.requestId); + if (n == null) { + n = new NetworkRequestInfo(request, score); + mNetworkRequests.put(n.request.requestId, n); + } else { + n.score = score; + } + if (DBG) log("got request " + request + " with score " + score); + if (DBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); + + evalRequest(n); + } + + private void handleRemoveRequest(NetworkRequest request) { + NetworkRequestInfo n = mNetworkRequests.get(request.requestId); + if (n != null && n.requested) { + mNetworkRequests.remove(request.requestId); + releaseNetworkFor(n.request); + } + } + + private void handleSetScore(int score) { + mScore = score; + evalRequests(); + } + + private void handleSetFilter(NetworkCapabilities netCap) { + mCapabilityFilter = netCap; + evalRequests(); + } + + /** + * Overridable function to provide complex filtering. + * Called for every request every time a new NetworkRequest is seen + * and whenever the filterScore or filterNetworkCapabilities change. + * + * acceptRequest can be overriden to provide complex filter behavior + * for the incoming requests + * + * For output, this class will call {@link #needNetworkFor} and + * {@link #releaseNetworkFor} for every request that passes the filters. + * If you don't need to see every request, you can leave the base + * implementations of those two functions and instead override + * {@link #startNetwork} and {@link #stopNetwork}. + * + * If you want to see every score fluctuation on every request, set + * your score filter to a very high number and watch {@link #needNetworkFor}. + * + * @return {@code true} to accept the request. + */ + public boolean acceptRequest(NetworkRequest request, int score) { + return true; + } + + private void evalRequest(NetworkRequestInfo n) { + if (n.requested == false && n.score < mScore && + n.request.networkCapabilities.satisfiedByNetworkCapabilities( + mCapabilityFilter) && acceptRequest(n.request, n.score)) { + needNetworkFor(n.request, n.score); + n.requested = true; + } else if (n.requested == true && + (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities( + mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) { + releaseNetworkFor(n.request); + n.requested = false; + } + } + + private void evalRequests() { + for (int i = 0; i < mNetworkRequests.size(); i++) { + NetworkRequestInfo n = mNetworkRequests.valueAt(i); + + evalRequest(n); + } + } + + // override to do simple mode (request independent) + protected void startNetwork() { } + protected void stopNetwork() { } + + // override to do fancier stuff + protected void needNetworkFor(NetworkRequest networkRequest, int score) { + if (++mRefCount == 1) startNetwork(); + } + + protected void releaseNetworkFor(NetworkRequest networkRequest) { + if (--mRefCount == 0) stopNetwork(); + } + + + public void addNetworkRequest(NetworkRequest networkRequest, int score) { + sendMessage(obtainMessage(CMD_REQUEST_NETWORK, + new NetworkRequestInfo(networkRequest, score))); + } + + public void removeNetworkRequest(NetworkRequest networkRequest) { + sendMessage(obtainMessage(CMD_CANCEL_REQUEST, networkRequest)); + } + + public void setScoreFilter(int score) { + sendMessage(obtainMessage(CMD_SET_SCORE, score, 0)); + } + + public void setCapabilityFilter(NetworkCapabilities netCap) { + sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); + } + + protected void log(String s) { + Log.d(LOG_TAG, s); + } +} diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 9e656eecbe4c..d2794127f542 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -188,6 +188,15 @@ public class NetworkInfo implements Parcelable { } /** + * @hide + */ + public void setType(int type) { + synchronized (this) { + mNetworkType = type; + } + } + + /** * Return a network-type-specific integer describing the subtype * of the network. * @return the network subtype @@ -198,7 +207,10 @@ public class NetworkInfo implements Parcelable { } } - void setSubtype(int subtype, String subtypeName) { + /** + * @hide + */ + public void setSubtype(int subtype, String subtypeName) { synchronized (this) { mSubtype = subtype; mSubtypeName = subtypeName; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 480cb057b075..47377e97be43 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -47,19 +47,19 @@ public class NetworkRequest implements Parcelable { public final int requestId; /** - * Set for legacy requests and the default. + * Set for legacy requests and the default. Set to TYPE_NONE for none. * Causes CONNECTIVITY_ACTION broadcasts to be sent. * @hide */ - public final boolean needsBroadcasts; + public final int legacyType; /** * @hide */ - public NetworkRequest(NetworkCapabilities nc, boolean needsBroadcasts, int rId) { + public NetworkRequest(NetworkCapabilities nc, int legacyType, int rId) { requestId = rId; networkCapabilities = nc; - this.needsBroadcasts = needsBroadcasts; + this.legacyType = legacyType; } /** @@ -68,7 +68,7 @@ public class NetworkRequest implements Parcelable { public NetworkRequest(NetworkRequest that) { networkCapabilities = new NetworkCapabilities(that.networkCapabilities); requestId = that.requestId; - needsBroadcasts = that.needsBroadcasts; + this.legacyType = that.legacyType; } // implement the Parcelable interface @@ -77,16 +77,16 @@ public class NetworkRequest implements Parcelable { } public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(networkCapabilities, flags); - dest.writeInt(needsBroadcasts ? 1 : 0); + dest.writeInt(legacyType); dest.writeInt(requestId); } public static final Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); - boolean needsBroadcasts = (in.readInt() == 1); + int legacyType = in.readInt(); int requestId = in.readInt(); - NetworkRequest result = new NetworkRequest(nc, needsBroadcasts, requestId); + NetworkRequest result = new NetworkRequest(nc, legacyType, requestId); return result; } public NetworkRequest[] newArray(int size) { @@ -95,14 +95,14 @@ public class NetworkRequest implements Parcelable { }; public String toString() { - return "NetworkRequest [ id=" + requestId + ", needsBroadcasts=" + needsBroadcasts + + return "NetworkRequest [ id=" + requestId + ", legacyType=" + legacyType + ", " + networkCapabilities.toString() + " ]"; } public boolean equals(Object obj) { if (obj instanceof NetworkRequest == false) return false; NetworkRequest that = (NetworkRequest)obj; - return (that.needsBroadcasts == this.needsBroadcasts && + return (that.legacyType == this.legacyType && that.requestId == this.requestId && ((that.networkCapabilities == null && this.networkCapabilities == null) || (that.networkCapabilities != null && @@ -110,7 +110,7 @@ public class NetworkRequest implements Parcelable { } public int hashCode() { - return requestId + (needsBroadcasts ? 1013 : 2026) + + return requestId + (legacyType * 1013) + (networkCapabilities.hashCode() * 1051); } } diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index e9181192892f..4979059abf16 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -219,6 +219,15 @@ public final class RenderNodeAnimator extends Animator { return mTarget; } + /** + * WARNING: May only be called once!!! + * TODO: Fix above -_- + */ + public void setStartValue(float startValue) { + checkMutable(); + nSetStartValue(mNativePtr.get(), startValue); + } + @Override public void setStartDelay(long startDelay) { checkMutable(); @@ -282,11 +291,12 @@ public final class RenderNodeAnimator extends Animator { } private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, - int property, float deltaValue); + int property, float finalValue); private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, - long canvasProperty, float deltaValue); + long canvasProperty, float finalValue); private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, - long canvasProperty, int paintField, float deltaValue); + long canvasProperty, int paintField, float finalValue); + private static native void nSetStartValue(long nativePtr, float startValue); private static native void nSetDuration(long nativePtr, long duration); private static native long nGetDuration(long nativePtr); private static native void nSetStartDelay(long nativePtr, long startDelay); diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 03d3b226425e..77f0dec00c71 100644 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -25,16 +25,18 @@ import android.content.res.ObbInfo; interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, - boolean isForwardLocked); + boolean isForwardLocked, in String abiOverride); int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold); + PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold, + in String abiOverride); boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); - boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked); + boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride); ObbInfo getObbInfo(in String filename); long calculateDirectorySize(in String directory); /** Return file system stats: [0] is total bytes, [1] is available bytes */ long[] getFileSystemStats(in String path); void clearDirectory(in String directory); - long calculateInstalledSize(in String packagePath, boolean isForwardLocked); + long calculateInstalledSize(in String packagePath, boolean isForwardLocked, + in String abiOverride); } diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java deleted file mode 100644 index 4c276b77cc29..000000000000 --- a/core/java/com/android/internal/backup/BackupConstants.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.backup; - -/** - * Constants used internally between the backup manager and its transports - */ -public class BackupConstants { - public static final int TRANSPORT_OK = 0; - public static final int TRANSPORT_ERROR = 1; - public static final int TRANSPORT_NOT_INITIALIZED = 2; - public static final int AGENT_ERROR = 3; - public static final int AGENT_UNKNOWN = 4; -} diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index f2b29ef69e25..7292116b6130 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -104,7 +104,7 @@ public class LocalTransport extends BackupTransport { public int initializeDevice() { if (DEBUG) Log.v(TAG, "wiping all data"); deleteContents(mCurrentSetDir); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) { @@ -166,7 +166,7 @@ public class LocalTransport extends BackupTransport { entity.write(buf, 0, dataSize); } catch (IOException e) { Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } finally { entity.close(); } @@ -174,11 +174,11 @@ public class LocalTransport extends BackupTransport { entityFile.delete(); } } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } catch (IOException e) { // oops, something went wrong. abort the operation and return error. Log.v(TAG, "Exception reading backup input:", e); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } } @@ -208,12 +208,12 @@ public class LocalTransport extends BackupTransport { } packageDir.delete(); } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public int finishBackup() { if (DEBUG) Log.v(TAG, "finishBackup()"); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } // Restore handling @@ -249,7 +249,7 @@ public class LocalTransport extends BackupTransport { mRestorePackage = -1; mRestoreToken = token; mRestoreDataDir = new File(mDataDir, Long.toString(token)); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public String nextRestorePackage() { @@ -281,7 +281,7 @@ public class LocalTransport extends BackupTransport { ArrayList<DecodedFilename> blobs = contentsByKey(packageDir); if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error Log.e(TAG, "No keys for package: " + packageDir); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } // We expect at least some data if the directory exists in the first place @@ -302,10 +302,10 @@ public class LocalTransport extends BackupTransport { in.close(); } } - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } catch (IOException e) { Log.e(TAG, "Unable to read backup records", e); - return BackupConstants.TRANSPORT_ERROR; + return BackupTransport.TRANSPORT_ERROR; } } diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index ba419f9bf1ab..dab3aff7c721 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -20,6 +20,7 @@ import android.content.pm.PackageManager; import android.util.Slog; import java.io.File; +import java.io.IOException; /** * Native libraries helper. @@ -141,4 +142,18 @@ public class NativeLibraryHelper { return deletedFiles; } + + // We don't care about the other return values for now. + private static final int BITCODE_PRESENT = 1; + + public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException { + final int returnVal = hasRenderscriptBitcode(handle.apkHandle); + if (returnVal < 0) { + throw new IOException("Error scanning APK, code: " + returnVal); + } + + return (returnVal == BITCODE_PRESENT); + } + + private static native int hasRenderscriptBitcode(long apkHandle); } diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index df9648862ca0..7dbde699b4fa 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.TreeMap; /** @@ -117,6 +118,24 @@ public class InputMethodSubtypeSwitchingController { + " mIsSystemLanguage=" + mIsSystemLanguage + "}"; } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof ImeSubtypeListItem) { + final ImeSubtypeListItem that = (ImeSubtypeListItem)o; + if (!Objects.equals(this.mImi, that.mImi)) { + return false; + } + if (this.mSubtypeId != that.mSubtypeId) { + return false; + } + return true; + } + return false; + } } private static class InputMethodAndSubtypeList { @@ -276,7 +295,7 @@ public class InputMethodSubtypeSwitchingController { private final List<ImeSubtypeListItem> mImeSubtypeList; private final int[] mUsageHistoryOfSubtypeListItemIndex; - public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) { + private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) { mImeSubtypeList = imeSubtypeListItems; mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()]; final int N = mImeSubtypeList.size(); @@ -347,15 +366,53 @@ public class InputMethodSubtypeSwitchingController { @VisibleForTesting public static class ControllerImpl { - // TODO: Switch to DynamicRotationList for smarter rotation. - private final StaticRotationList mSwitchingAwareSubtypeList; - private final StaticRotationList mSwitchingUnawareSubtypeList; - - public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) { - mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, - true /* supportsSwitchingToNextInputMethod */)); - mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems, - false /* supportsSwitchingToNextInputMethod */)); + private final DynamicRotationList mSwitchingAwareRotationList; + private final StaticRotationList mSwitchingUnawareRotationList; + + public static ControllerImpl createFrom(final ControllerImpl currentInstance, + final List<ImeSubtypeListItem> sortedEnabledItems) { + DynamicRotationList switchingAwareRotationList = null; + { + final List<ImeSubtypeListItem> switchingAwareImeSubtypes = + filterImeSubtypeList(sortedEnabledItems, + true /* supportsSwitchingToNextInputMethod */); + if (currentInstance != null && + currentInstance.mSwitchingAwareRotationList != null && + Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList, + switchingAwareImeSubtypes)) { + // Can reuse the current instance. + switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList; + } + if (switchingAwareRotationList == null) { + switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes); + } + } + + StaticRotationList switchingUnawareRotationList = null; + { + final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList( + sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */); + if (currentInstance != null && + currentInstance.mSwitchingUnawareRotationList != null && + Objects.equals( + currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList, + switchingUnawareImeSubtypes)) { + // Can reuse the current instance. + switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList; + } + if (switchingUnawareRotationList == null) { + switchingUnawareRotationList = + new StaticRotationList(switchingUnawareImeSubtypes); + } + } + + return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList); + } + + private ControllerImpl(final DynamicRotationList switchingAwareRotationList, + final StaticRotationList switchingUnawareRotationList) { + mSwitchingAwareRotationList = switchingAwareRotationList; + mSwitchingUnawareRotationList = switchingUnawareRotationList; } public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi, @@ -364,10 +421,10 @@ public class InputMethodSubtypeSwitchingController { return null; } if (imi.supportsSwitchingToNextInputMethod()) { - return mSwitchingAwareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi, subtype); } else { - return mSwitchingUnawareSubtypeList.getNextInputMethodLocked(onlyCurrentIme, imi, + return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi, subtype); } } @@ -376,10 +433,9 @@ public class InputMethodSubtypeSwitchingController { if (imi == null) { return; } - // TODO: Enable the following code when DynamicRotationList is enabled. - // if (imi.supportsSwitchingToNextInputMethod()) { - // mSwitchingAwareSubtypeList.onUserAction(imi, subtype); - // } + if (imi.supportsSwitchingToNextInputMethod()) { + mSwitchingAwareRotationList.onUserAction(imi, subtype); + } } private static List<ImeSubtypeListItem> filterImeSubtypeList( @@ -424,7 +480,8 @@ public class InputMethodSubtypeSwitchingController { public void resetCircularListLocked(Context context) { mSubtypeList = new InputMethodAndSubtypeList(context, mSettings); - mController = new ControllerImpl(mSubtypeList.getSortedInputMethodAndSubtypeList()); + mController = ControllerImpl.createFrom(mController, + mSubtypeList.getSortedInputMethodAndSubtypeList()); } public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index a56fa36ab983..d66ef83ebcad 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -169,6 +169,15 @@ public class ArrayUtils return false; } + public static boolean contains(long[] array, long value) { + for (long element : array) { + if (element == value) { + return true; + } + } + return false; + } + public static long total(long[] array) { long total = 0; for (long value : array) { @@ -229,6 +238,14 @@ public class ArrayUtils return array; } + /** + * Appends a new value to a copy of the array and returns the copy. If + * the value is already present, the original array is returned + * @param cur The original array, or null to represent an empty array. + * @param val The value to add. + * @return A new array that contains all of the values of the original array + * with the new value added, or the original array. + */ public static int[] appendInt(int[] cur, int val) { if (cur == null) { return new int[] { val }; @@ -264,4 +281,48 @@ public class ArrayUtils } return cur; } + + /** + * Appends a new value to a copy of the array and returns the copy. If + * the value is already present, the original array is returned + * @param cur The original array, or null to represent an empty array. + * @param val The value to add. + * @return A new array that contains all of the values of the original array + * with the new value added, or the original array. + */ + public static long[] appendLong(long[] cur, long val) { + if (cur == null) { + return new long[] { val }; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + return cur; + } + } + long[] ret = new long[N + 1]; + System.arraycopy(cur, 0, ret, 0, N); + ret[N] = val; + return ret; + } + + public static long[] removeLong(long[] cur, long val) { + if (cur == null) { + return null; + } + final int N = cur.length; + for (int i = 0; i < N; i++) { + if (cur[i] == val) { + long[] ret = new long[N - 1]; + if (i > 0) { + System.arraycopy(cur, 0, ret, 0, i); + } + if (i < (N - 1)) { + System.arraycopy(cur, i + 1, ret, i, N - i - 1); + } + return ret; + } + } + return cur; + } } diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 81e67d86d626..af966b194e56 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -57,9 +57,9 @@ public class Protocol { public static final int BASE_DNS_PINGER = 0x00050000; public static final int BASE_NSD_MANAGER = 0x00060000; public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000; - public static final int BASE_CONNECTIVITY_SERVICE = 0x00080000; + public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000; public static final int BASE_NETWORK_AGENT = 0x00081000; public static final int BASE_NETWORK_MONITOR = 0x00082000; - public static final int BASE_CONNECTIVITY_MANAGER = 0x00083000; + public static final int BASE_NETWORK_FACTORY = 0x00083000; //TODO: define all used protocols } diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index e19ce36e14f9..d689864eec8b 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -116,6 +116,11 @@ static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, return reinterpret_cast<jlong>( animator ); } +static void setStartValue(JNIEnv* env, jobject clazz, jlong animatorPtr, jfloat startValue) { + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); + animator->setStartValue(startValue); +} + static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong duration) { LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative"); BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); @@ -157,6 +162,7 @@ static JNINativeMethod gMethods[] = { { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator }, { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator }, { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator }, + { "nSetStartValue", "(JF)V", (void*) setStartValue }, { "nSetDuration", "(JJ)V", (void*) setDuration }, { "nGetDuration", "(J)J", (void*) getDuration }, { "nSetStartDelay", "(JJ)V", (void*) setStartDelay }, diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index 230658f5c093..e55e4ea2af1d 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -46,6 +46,9 @@ #define LIB_SUFFIX ".so" #define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1) +#define RS_BITCODE_SUFFIX ".bc" +#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1) + #define GDBSERVER "gdbserver" #define GDBSERVER_LEN (sizeof(GDBSERVER) - 1) @@ -486,6 +489,42 @@ com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, j return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch); } +enum bitcode_scan_result_t { + APK_SCAN_ERROR = -1, + NO_BITCODE_PRESENT = 0, + BITCODE_PRESENT = 1, +}; + +static jint +com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz, + jlong apkHandle) { + ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle); + void* cookie = NULL; + if (!zipFile->startIteration(&cookie)) { + return APK_SCAN_ERROR; + } + + char fileName[PATH_MAX]; + ZipEntryRO next = NULL; + while ((next = zipFile->nextEntry(cookie)) != NULL) { + if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) { + continue; + } + + const size_t fileNameLen = strlen(fileName); + const char* lastSlash = strrchr(fileName, '/'); + const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1; + if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX, + RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) { + zipFile->endIteration(cookie); + return BITCODE_PRESENT; + } + } + + zipFile->endIteration(cookie); + return NO_BITCODE_PRESENT; +} + static jlong com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath) { @@ -517,6 +556,8 @@ static JNINativeMethod gMethods[] = { {"nativeFindSupportedAbi", "(J[Ljava/lang/String;)I", (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi}, + {"hasRenderscriptBitcode", "(J)I", + (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode}, }; diff --git a/core/res/res/anim/voice_activity_open_enter.xml b/core/res/res/anim/voice_activity_open_enter.xml index 57fba2a38461..ce7a4f9f4cd1 100644 --- a/core/res/res/anim/voice_activity_open_enter.xml +++ b/core/res/res/anim/voice_activity_open_enter.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/anim/fade_in.xml ** ** Copyright 2007, The Android Open Source Project ** diff --git a/core/res/res/anim/voice_layer_enter.xml b/core/res/res/anim/voice_layer_enter.xml index 57fba2a38461..ce7a4f9f4cd1 100644 --- a/core/res/res/anim/voice_layer_enter.xml +++ b/core/res/res/anim/voice_layer_enter.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -/* //device/apps/common/res/anim/fade_in.xml ** ** Copyright 2007, The Android Open Source Project ** diff --git a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png Binary files differdeleted file mode 100644 index ba5bd01fc7f3..000000000000 --- a/core/res/res/drawable-hdpi/ic_lock_bugreport_alpha.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png Binary files differdeleted file mode 100644 index 4e2612df11c2..000000000000 --- a/core/res/res/drawable-mdpi/ic_lock_bugreport_alpha.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png Binary files differdeleted file mode 100644 index e6ca1ea1d623..000000000000 --- a/core/res/res/drawable-xhdpi/ic_lock_bugreport_alpha.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png b/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png Binary files differdeleted file mode 100644 index d6018dd5d8f0..000000000000 --- a/core/res/res/drawable-xxhdpi/ic_lock_bugreport_alpha.png +++ /dev/null diff --git a/core/res/res/drawable/ic_lock_bugreport.xml b/core/res/res/drawable/ic_lock_bugreport.xml index a3f82ce92589..b93a09a8e1ec 100644 --- a/core/res/res/drawable/ic_lock_bugreport.xml +++ b/core/res/res/drawable/ic_lock_bugreport.xml @@ -1,19 +1,28 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- +Copyright (C) 2014 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="32dp" + android:height="32dp"/> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_lock_bugreport_alpha" - android:tint="?attr/colorControlNormal" /> + <viewport + android:viewportWidth="24.0" + android:viewportHeight="24.0"/> + + <path + android:fill="?attr/colorControlNormal" + android:pathData="M20.0,8.0l-2.8,0.0c-0.5,-0.8 -1.1,-1.5 -1.8,-2.0L17.0,4.4L15.6,3.0l-2.2,2.2C13.0,5.1 12.5,5.0 12.0,5.0s-1.0,0.1 -1.4,0.2L8.4,3.0L7.0,4.4L8.6,6.0C7.9,6.5 7.3,7.2 6.8,8.0L4.0,8.0l0.0,2.0l2.1,0.0C6.0,10.3 6.0,10.7 6.0,11.0l0.0,1.0L4.0,12.0l0.0,2.0l2.0,0.0l0.0,1.0c0.0,0.3 0.0,0.7 0.1,1.0L4.0,16.0l0.0,2.0l2.8,0.0c1.0,1.8 3.0,3.0 5.2,3.0s4.2,-1.2 5.2,-3.0L20.0,18.0l0.0,-2.0l-2.1,0.0c0.1,-0.3 0.1,-0.7 0.1,-1.0l0.0,-1.0l2.0,0.0l0.0,-2.0l-2.0,0.0l0.0,-1.0c0.0,-0.3 0.0,-0.7 -0.1,-1.0L20.0,10.0L20.0,8.0zM14.0,16.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,16.0zM14.0,12.0l-4.0,0.0l0.0,-2.0l4.0,0.0L14.0,12.0z"/> +</vector> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8286ef9d8b94..7234911bf655 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -419,6 +419,8 @@ current device state, to send as an e-mail message. It will take a little time from starting the bug report until it is ready to be sent; please be patient.</string> + <!-- Format for build summary info [CHAR LIMIT=NONE] --> + <string name="bugreport_status" translatable="false">%s (%s)</string> <!-- label for item that enables silent mode in phone options dialog --> <string name="global_action_toggle_silent_mode">Silent mode</string> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 2f51048a89f8..108334faf6cf 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -518,7 +518,10 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.ExpandableListView.White"/> <style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/> <style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/> - <style name="Widget.Quantum.GridView" parent="Widget.GridView"/> + + <style name="Widget.Quantum.GridView" parent="Widget.GridView"> + <item name="android:listSelector">?attr/selectableItemBackground</item> + </style> <style name="Widget.Quantum.CalendarView" parent="Widget.CalendarView"> <item name="selectedWeekBackgroundColor">#330099FF</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6cd7cd2cf5f9..8b1ca31eccfc 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1388,6 +1388,7 @@ <java-symbol type="string" name="android_upgrading_title" /> <java-symbol type="string" name="bugreport_title" /> <java-symbol type="string" name="bugreport_message" /> + <java-symbol type="string" name="bugreport_status" /> <java-symbol type="string" name="faceunlock_multiple_failures" /> <java-symbol type="string" name="global_action_power_off" /> <java-symbol type="string" name="global_actions_airplane_mode_off_status" /> diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java index 5dc9ef8a87a1..433d4d214b97 100644 --- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java @@ -16,6 +16,9 @@ package com.android.internal.util; +import android.test.MoreAsserts; + +import java.util.Arrays; import junit.framework.TestCase; /** @@ -77,4 +80,79 @@ public class ArrayUtilsTest extends TestCase { assertFalse(ArrayUtils.containsAll(new Object[] { }, new Object[] { null })); assertFalse(ArrayUtils.containsAll(new Object[] { A }, new Object[] { null })); } + + public void testContainsInt() throws Exception { + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 1)); + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 2)); + assertTrue(ArrayUtils.contains(new int[] { 1, 2, 3 }, 3)); + + assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 0)); + assertFalse(ArrayUtils.contains(new int[] { 1, 2, 3 }, 4)); + assertFalse(ArrayUtils.contains(new int[] { }, 2)); + } + + public void testAppendInt() throws Exception { + MoreAsserts.assertEquals(new int[] { 1 }, + ArrayUtils.appendInt(null, 1)); + MoreAsserts.assertEquals(new int[] { 1 }, + ArrayUtils.appendInt(new int[] { }, 1)); + MoreAsserts.assertEquals(new int[] { 1, 2 }, + ArrayUtils.appendInt(new int[] { 1 }, 2)); + MoreAsserts.assertEquals(new int[] { 1, 2 }, + ArrayUtils.appendInt(new int[] { 1, 2 }, 1)); + } + + public void testRemoveInt() throws Exception { + assertNull(ArrayUtils.removeInt(null, 1)); + MoreAsserts.assertEquals(new int[] { }, + ArrayUtils.removeInt(new int[] { }, 1)); + MoreAsserts.assertEquals(new int[] { 1, 2, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 4)); + MoreAsserts.assertEquals(new int[] { 2, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 1)); + MoreAsserts.assertEquals(new int[] { 1, 3, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 2)); + MoreAsserts.assertEquals(new int[] { 1, 2, }, + ArrayUtils.removeInt(new int[] { 1, 2, 3}, 3)); + MoreAsserts.assertEquals(new int[] { 2, 3, 1 }, + ArrayUtils.removeInt(new int[] { 1, 2, 3, 1 }, 1)); + } + + public void testContainsLong() throws Exception { + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 1)); + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 2)); + assertTrue(ArrayUtils.contains(new long[] { 1, 2, 3 }, 3)); + + assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 0)); + assertFalse(ArrayUtils.contains(new long[] { 1, 2, 3 }, 4)); + assertFalse(ArrayUtils.contains(new long[] { }, 2)); + } + + public void testAppendLong() throws Exception { + MoreAsserts.assertEquals(new long[] { 1 }, + ArrayUtils.appendLong(null, 1)); + MoreAsserts.assertEquals(new long[] { 1 }, + ArrayUtils.appendLong(new long[] { }, 1)); + MoreAsserts.assertEquals(new long[] { 1, 2 }, + ArrayUtils.appendLong(new long[] { 1 }, 2)); + MoreAsserts.assertEquals(new long[] { 1, 2 }, + ArrayUtils.appendLong(new long[] { 1, 2 }, 1)); + } + + public void testRemoveLong() throws Exception { + assertNull(ArrayUtils.removeLong(null, 1)); + MoreAsserts.assertEquals(new long[] { }, + ArrayUtils.removeLong(new long[] { }, 1)); + MoreAsserts.assertEquals(new long[] { 1, 2, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 4)); + MoreAsserts.assertEquals(new long[] { 2, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 1)); + MoreAsserts.assertEquals(new long[] { 1, 3, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 2)); + MoreAsserts.assertEquals(new long[] { 1, 2, }, + ArrayUtils.removeLong(new long[] { 1, 2, 3}, 3)); + MoreAsserts.assertEquals(new long[] { 2, 3, 1 }, + ArrayUtils.removeLong(new long[] { 1, 2, 3, 1 }, 1)); + } + } diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java index c0c20e2ff9f1..ca68e939e635 100644 --- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java +++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java @@ -172,7 +172,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); - final ControllerImpl controller = new ControllerImpl(enabledItems); + final ControllerImpl controller = ControllerImpl.createFrom( + null /* currentInstance */, enabledItems); // switching-aware loop assertRotationOrder(controller, false /* onlyCurrentIme */, @@ -214,9 +215,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe disabledSubtypeUnawareIme, null); } - // This test is disabled until DynamicRotationList is enabled. @SmallTest - public void DISABLED_testControllerImplWithUserAction() throws Exception { + public void testControllerImplWithUserAction() throws Exception { final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes(); final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0); final ImeSubtypeListItem latinIme_fr = enabledItems.get(1); @@ -226,7 +226,8 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5); final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6); - final ControllerImpl controller = new ControllerImpl(enabledItems); + final ControllerImpl controller = ControllerImpl.createFrom( + null /* currentInstance */, enabledItems); // === switching-aware loop === assertRotationOrder(controller, false /* onlyCurrentIme */, @@ -272,5 +273,26 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe subtypeUnawareIme, null); assertNextInputMethod(controller, true /* onlyCurrentIme */, switchUnawareJapaneseIme_ja_JP, null); + + // Rotation order should be preserved when created with the same subtype list. + final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes(); + final ControllerImpl newController = ControllerImpl.createFrom(controller, + sameEnabledItems); + assertRotationOrder(newController, false /* onlyCurrentIme */, + japaneseIme_ja_JP, latinIme_fr, latinIme_en_US); + assertRotationOrder(newController, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme, + switchUnawareJapaneseIme_ja_JP); + + // Rotation order should be initialized when created with a different subtype list. + final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList( + latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK, + switchUnawareJapaneseIme_ja_JP); + final ControllerImpl anotherController = ControllerImpl.createFrom(controller, + differentEnabledItems); + assertRotationOrder(anotherController, false /* onlyCurrentIme */, + latinIme_en_US, latinIme_fr); + assertRotationOrder(anotherController, false /* onlyCurrentIme */, + switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP); } } diff --git a/data/fonts/Roboto-Black.ttf b/data/fonts/Roboto-Black.ttf Binary files differindex 2cdbe43e0203..cb905bcd83e9 100644 --- a/data/fonts/Roboto-Black.ttf +++ b/data/fonts/Roboto-Black.ttf diff --git a/data/fonts/Roboto-Bold.ttf b/data/fonts/Roboto-Bold.ttf Binary files differindex 15c9b4e3f028..68822caf2421 100644 --- a/data/fonts/Roboto-Bold.ttf +++ b/data/fonts/Roboto-Bold.ttf diff --git a/data/fonts/Roboto-BoldItalic.ttf b/data/fonts/Roboto-BoldItalic.ttf Binary files differindex a0abf3031829..aebf8eb027a6 100644 --- a/data/fonts/Roboto-BoldItalic.ttf +++ b/data/fonts/Roboto-BoldItalic.ttf diff --git a/data/fonts/Roboto-Italic.ttf b/data/fonts/Roboto-Italic.ttf Binary files differindex 67b53946932a..2041cbc002be 100644 --- a/data/fonts/Roboto-Italic.ttf +++ b/data/fonts/Roboto-Italic.ttf diff --git a/data/fonts/Roboto-Light.ttf b/data/fonts/Roboto-Light.ttf Binary files differindex d9fb64a61f8a..aa4534075781 100644 --- a/data/fonts/Roboto-Light.ttf +++ b/data/fonts/Roboto-Light.ttf diff --git a/data/fonts/Roboto-LightItalic.ttf b/data/fonts/Roboto-LightItalic.ttf Binary files differindex 1fd1d3117345..a85444f2ecec 100644 --- a/data/fonts/Roboto-LightItalic.ttf +++ b/data/fonts/Roboto-LightItalic.ttf diff --git a/data/fonts/Roboto-Medium.ttf b/data/fonts/Roboto-Medium.ttf Binary files differindex c63c115ea5be..a3c1a1f1702e 100644 --- a/data/fonts/Roboto-Medium.ttf +++ b/data/fonts/Roboto-Medium.ttf diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf Binary files differindex cd7c83535730..a30aa0cf5dbf 100644 --- a/data/fonts/Roboto-MediumItalic.ttf +++ b/data/fonts/Roboto-MediumItalic.ttf diff --git a/data/fonts/Roboto-Regular.ttf b/data/fonts/Roboto-Regular.ttf Binary files differindex 9cb4a5abd7d1..0e58508a64bf 100644 --- a/data/fonts/Roboto-Regular.ttf +++ b/data/fonts/Roboto-Regular.ttf diff --git a/data/fonts/Roboto-Thin.ttf b/data/fonts/Roboto-Thin.ttf Binary files differindex f02f10059d96..8779333b1a25 100644 --- a/data/fonts/Roboto-Thin.ttf +++ b/data/fonts/Roboto-Thin.ttf diff --git a/data/fonts/Roboto-ThinItalic.ttf b/data/fonts/Roboto-ThinItalic.ttf Binary files differindex 12a2ce0a3dd8..b79cb26da037 100644 --- a/data/fonts/Roboto-ThinItalic.ttf +++ b/data/fonts/Roboto-ThinItalic.ttf diff --git a/data/fonts/RobotoCondensed-Bold.ttf b/data/fonts/RobotoCondensed-Bold.ttf Binary files differindex 1079af64cffe..3e06c7cdc4ce 100644 --- a/data/fonts/RobotoCondensed-Bold.ttf +++ b/data/fonts/RobotoCondensed-Bold.ttf diff --git a/data/fonts/RobotoCondensed-BoldItalic.ttf b/data/fonts/RobotoCondensed-BoldItalic.ttf Binary files differindex e7f13c2a474e..aaf9fe03bc8a 100644 --- a/data/fonts/RobotoCondensed-BoldItalic.ttf +++ b/data/fonts/RobotoCondensed-BoldItalic.ttf diff --git a/data/fonts/RobotoCondensed-Italic.ttf b/data/fonts/RobotoCondensed-Italic.ttf Binary files differindex 7fa04481e59e..d2b611feadbe 100644 --- a/data/fonts/RobotoCondensed-Italic.ttf +++ b/data/fonts/RobotoCondensed-Italic.ttf diff --git a/data/fonts/RobotoCondensed-Light.ttf b/data/fonts/RobotoCondensed-Light.ttf Binary files differindex 96b75dde0e36..d4eb1980614e 100644 --- a/data/fonts/RobotoCondensed-Light.ttf +++ b/data/fonts/RobotoCondensed-Light.ttf diff --git a/data/fonts/RobotoCondensed-LightItalic.ttf b/data/fonts/RobotoCondensed-LightItalic.ttf Binary files differindex 7a2c164e734d..a08f3f47038f 100644 --- a/data/fonts/RobotoCondensed-LightItalic.ttf +++ b/data/fonts/RobotoCondensed-LightItalic.ttf diff --git a/data/fonts/RobotoCondensed-Regular.ttf b/data/fonts/RobotoCondensed-Regular.ttf Binary files differindex 734cc40d0283..b9fc49c95b06 100644 --- a/data/fonts/RobotoCondensed-Regular.ttf +++ b/data/fonts/RobotoCondensed-Regular.ttf diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd index e9caa44ed38b..e748b12d06cb 100644 --- a/docs/html/tools/device.jd +++ b/docs/html/tools/device.jd @@ -280,6 +280,10 @@ above.</p> <td><code>0fce</code></td> </tr> <tr> + <td>Sony Mobile Communications</td> + <td><code>0fce</code></td> + </tr> + <tr> <td>Teleepoch</td> <td><code>2340</code></td> </tr> diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index b2d61ec0fbbe..1512da5b3ff8 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -612,7 +612,9 @@ public class GradientDrawable extends Drawable { case LINE: { RectF r = mRect; float y = r.centerY(); - canvas.drawLine(r.left, y, r.right, y, mStrokePaint); + if (haveStroke) { + canvas.drawLine(r.left, y, r.right, y, mStrokePaint); + } break; } case RING: @@ -1431,7 +1433,7 @@ public class GradientDrawable extends Drawable { public int mChangingConfigurations; public int mShape = RECTANGLE; public int mGradient = LINEAR_GRADIENT; - public int mAngle; + public int mAngle = 0; public Orientation mOrientation; public ColorStateList mColorStateList; public ColorStateList mStrokeColorStateList; @@ -1439,12 +1441,12 @@ public class GradientDrawable extends Drawable { public int[] mTempColors; // no need to copy public float[] mTempPositions; // no need to copy public float[] mPositions; - public int mStrokeWidth = -1; // if >= 0 use stroking. - public float mStrokeDashWidth; - public float mStrokeDashGap; - public float mRadius; // use this if mRadiusArray is null - public float[] mRadiusArray; - public Rect mPadding; + public int mStrokeWidth = -1; // if >= 0 use stroking. + public float mStrokeDashWidth = 0.0f; + public float mStrokeDashGap = 0.0f; + public float mRadius = 0.0f; // use this if mRadiusArray is null + public float[] mRadiusArray = null; + public Rect mPadding = null; public int mWidth = -1; public int mHeight = -1; public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO; diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 442f327b0e1a..e5c8898c6d74 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -71,8 +71,6 @@ ifeq ($(USE_OPENGL_RENDERER),true) $(LOCAL_PATH)/../../include/utils \ external/skia/src/core - include external/stlport/libstlport.mk - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -80,16 +78,15 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional + include external/stlport/libstlport.mk + ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT - LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport + LOCAL_SHARED_LIBRARIES += libRS libRScpp LOCAL_C_INCLUDES += \ $(intermediates) \ frameworks/rs/cpp \ - frameworks/rs \ - external/stlport/stlport \ - bionic/ \ - bionic/libstdc++/include + frameworks/rs endif ifndef HWUI_COMPILE_SYMBOLS diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index b80f7e936943..eff3011685a8 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -63,7 +63,6 @@ void BaseRenderNodeAnimator::setStartValue(float value) { void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) { if (mPlayState == NEEDS_START) { setStartValue(getValue(target)); - mPlayState = PENDING; } } @@ -154,7 +153,8 @@ RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float fi } void RenderPropertyAnimator::onAttached(RenderNode* target) { - if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + if (mPlayState == NEEDS_START + && target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { setStartValue((target->stagingProperties().*mPropertyAccess->getter)()); } (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 7741617d28e3..a0c7c554bda2 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -41,6 +41,7 @@ protected: class BaseRenderNodeAnimator : public VirtualLightRefBase { PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); public: + ANDROID_API void setStartValue(float value); ANDROID_API void setInterpolator(Interpolator* interpolator); ANDROID_API void setDuration(nsecs_t durationInMs); ANDROID_API nsecs_t duration() { return mDuration; } @@ -64,11 +65,9 @@ protected: BaseRenderNodeAnimator(float finalValue); virtual ~BaseRenderNodeAnimator(); - void setStartValue(float value); virtual float getValue(RenderNode* target) const = 0; virtual void setValue(RenderNode* target, float value) = 0; -private: void callOnFinishedListener(TreeInfo& info); enum PlayState { diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 02f686f9e789..9212b9de766f 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -788,7 +788,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING, + OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING, mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds)); } @@ -978,7 +978,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPatch"; } @@ -1060,7 +1060,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, @@ -1111,7 +1111,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); + OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); } virtual const char* name() { return "DrawRoundRect"; } @@ -1175,7 +1175,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawOval"; } @@ -1195,7 +1195,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d", + OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d", RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter); } @@ -1232,7 +1232,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPath"; } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index a0ff0740fe84..84d4ab66fe32 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -644,7 +644,12 @@ public class AudioManager { if (mUseMasterVolume) { service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); } else { - service.adjustVolume(direction, flags, mContext.getOpPackageName()); + if (USE_SESSIONS) { + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); + } else { + service.adjustVolume(direction, flags, mContext.getOpPackageName()); + } } } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustVolume", e); @@ -674,8 +679,13 @@ public class AudioManager { if (mUseMasterVolume) { service.adjustMasterVolume(direction, flags, mContext.getOpPackageName()); } else { - service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, - mContext.getOpPackageName()); + if (USE_SESSIONS) { + MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); + helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); + } else { + service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, + mContext.getOpPackageName()); + } } } catch (RemoteException e) { Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e); diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java index cd9a4de9e141..782ecd845d4b 100644 --- a/media/java/android/media/AudioPortEventHandler.java +++ b/media/java/android/media/AudioPortEventHandler.java @@ -49,73 +49,77 @@ class AudioPortEventHandler { // find the looper for our new event handler Looper looper = Looper.myLooper(); if (looper == null) { - throw new IllegalArgumentException("Calling thread not associated with a looper"); + looper = Looper.getMainLooper(); } - mHandler = new Handler(looper) { - @Override - public void handleMessage(Message msg) { - Log.i(TAG, "handleMessage: "+msg.what); - ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; - synchronized (this) { - if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { - listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); - if (mListeners.contains(msg.obj)) { - listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); + if (looper != null) { + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + Log.i(TAG, "handleMessage: "+msg.what); + ArrayList<AudioManager.OnAudioPortUpdateListener> listeners; + synchronized (this) { + if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) { + listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>(); + if (mListeners.contains(msg.obj)) { + listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); + } + } else { + listeners = mListeners; } - } else { - listeners = mListeners; } - } - if (listeners.isEmpty()) { - return; - } - // reset audio port cache if the event corresponds to a change coming - // from audio policy service or if mediaserver process died. - if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || - msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED || - msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { - mAudioManager.resetAudioPortGeneration(); - } - ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); - ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); - if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { - int status = mAudioManager.updateAudioPortCache(ports, patches); - if (status != AudioManager.SUCCESS) { + if (listeners.isEmpty()) { return; } - } - - switch (msg.what) { - case AUDIOPORT_EVENT_NEW_LISTENER: - case AUDIOPORT_EVENT_PORT_LIST_UPDATED: - AudioPort[] portList = ports.toArray(new AudioPort[0]); - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).OnAudioPortListUpdate(portList); + // reset audio port cache if the event corresponds to a change coming + // from audio policy service or if mediaserver process died. + if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED || + msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED || + msg.what == AUDIOPORT_EVENT_SERVICE_DIED) { + mAudioManager.resetAudioPortGeneration(); } - if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) { - break; + ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); + ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); + if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) { + int status = mAudioManager.updateAudioPortCache(ports, patches); + if (status != AudioManager.SUCCESS) { + return; + } } - // FALL THROUGH - case AUDIOPORT_EVENT_PATCH_LIST_UPDATED: - AudioPatch[] patchList = patches.toArray(new AudioPatch[0]); - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).OnAudioPatchListUpdate(patchList); - } - break; + switch (msg.what) { + case AUDIOPORT_EVENT_NEW_LISTENER: + case AUDIOPORT_EVENT_PORT_LIST_UPDATED: + AudioPort[] portList = ports.toArray(new AudioPort[0]); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).OnAudioPortListUpdate(portList); + } + if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) { + break; + } + // FALL THROUGH - case AUDIOPORT_EVENT_SERVICE_DIED: - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).OnServiceDied(); - } - break; + case AUDIOPORT_EVENT_PATCH_LIST_UPDATED: + AudioPatch[] patchList = patches.toArray(new AudioPatch[0]); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).OnAudioPatchListUpdate(patchList); + } + break; + + case AUDIOPORT_EVENT_SERVICE_DIED: + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).OnServiceDied(); + } + break; - default: - break; + default: + break; + } } - } - }; + }; + } else { + mHandler = null; + } native_setup(new WeakReference<AudioPortEventHandler>(this)); } diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index c4233c305192..1cfc5bc3f14a 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -47,4 +47,8 @@ interface ISession { void setMetadata(in MediaMetadata metadata); void setPlaybackState(in PlaybackState state); void setRatingType(int type); + + // These commands relate to volume handling + void configureVolumeHandling(int type, int arg1, int arg2); + void setCurrentVolume(int currentVolume); }
\ No newline at end of file diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index 103c3f160e4e..0316d1fa5544 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -45,4 +45,8 @@ oneway interface ISessionCallback { void onRewind(); void onSeekTo(long pos); void onRate(in Rating rating); + + // These callbacks are for volume handling + void onAdjustVolumeBy(int delta); + void onSetVolumeTo(int value); }
\ No newline at end of file diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 38b929327f46..6d9888f09c14 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -29,4 +29,5 @@ interface ISessionManager { ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId); List<IBinder> getSessions(in ComponentName compName, int userId); void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock); + void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags); }
\ No newline at end of file diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 90ccf689fe88..7972639a97bf 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -124,9 +124,21 @@ public final class MediaSession { */ public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5; - private static final String KEY_COMMAND = "command"; - private static final String KEY_EXTRAS = "extras"; - private static final String KEY_CALLBACK = "callback"; + /** + * The session uses local playback. Used for configuring volume handling + * with the system. + * + * @hide + */ + public static final int VOLUME_TYPE_LOCAL = 1; + + /** + * The session uses remote playback. Used for configuring volume handling + * with the system. + * + * @hide + */ + public static final int VOLUME_TYPE_REMOTE = 2; private final Object mLock = new Object(); @@ -143,6 +155,7 @@ public final class MediaSession { = new ArrayMap<String, RouteInterface.EventListener>(); private Route mRoute; + private RemoteVolumeProvider mVolumeProvider; private boolean mActive = false;; @@ -242,7 +255,11 @@ public final class MediaSession { * @param stream The {@link AudioManager} stream this session is playing on. */ public void setPlaybackToLocal(int stream) { - // TODO + try { + mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); + } } /** @@ -259,7 +276,14 @@ public final class MediaSession { if (volumeProvider == null) { throw new IllegalArgumentException("volumeProvider may not be null!"); } - // TODO + mVolumeProvider = volumeProvider; + + try { + mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(), + volumeProvider.getMaxVolume()); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); + } } /** @@ -942,6 +966,26 @@ public final class MediaSession { } + /* + * (non-Javadoc) + * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int) + */ + @Override + public void onAdjustVolumeBy(int delta) throws RemoteException { + // TODO(epastern): Auto-generated method stub + + } + + /* + * (non-Javadoc) + * @see android.media.session.ISessionCallback#onSetVolumeTo(int) + */ + @Override + public void onSetVolumeTo(int value) throws RemoteException { + // TODO(epastern): Auto-generated method stub + + } + } private class CallbackMessageHandler extends Handler { diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java index c303e77ee00e..099f60152c65 100644 --- a/media/java/android/media/session/MediaSessionLegacyHelper.java +++ b/media/java/android/media/session/MediaSessionLegacyHelper.java @@ -76,6 +76,13 @@ public class MediaSessionLegacyHelper { } } + public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) { + mSessionManager.dispatchAdjustVolumeBy(suggestedStream, delta, flags); + if (DEBUG) { + Log.d(TAG, "dispatched volume adjustment"); + } + } + public void addRccListener(PendingIntent pi, MediaSession.TransportControlsCallback listener) { if (pi == null) { diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 8d5e338f8db0..9e8b0d36b5b2 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -166,4 +166,22 @@ public final class MediaSessionManager { Log.e(TAG, "Failed to send key event.", e); } } + + /** + * Dispatch an adjust volume request to the system. It will be routed to the + * most relevant stream/session. + * + * @param suggestedStream The stream to fall back to if there isn't a + * relevant stream + * @param delta The amount to adjust the volume by. + * @param flags Any flags to include with the volume change. + * @hide + */ + public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) { + try { + mService.dispatchAdjustVolumeBy(suggestedStream, delta, flags); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send adjust volume.", e); + } + } } diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index d7813368310e..19b54a6d8f78 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -433,16 +433,14 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, case MTP_TYPE_STR: { jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); + const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL); if (stringValue) { - const char* str = env->GetStringUTFChars(stringValue, NULL); - if (str == NULL) { - return MTP_RESPONSE_GENERAL_ERROR; - } packet.putString(str); env->ReleaseStringUTFChars(stringValue, str); } else { packet.putEmptyString(); } + env->DeleteLocalRef(stringValue); break; } default: @@ -515,7 +513,7 @@ MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle, break; } default: - ALOGE("unsupported type in getObjectPropertyValue\n"); + ALOGE("unsupported type in setObjectPropertyValue\n"); return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 36c1d5c704d2..ec87c6e75549 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -88,7 +88,7 @@ public class DefaultContainerService extends IntentService { private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { /** * Creates a new container and copies resource there. - * @param paackageURI the uri of resource to be copied. Can be either + * @param packageURI the uri of resource to be copied. Can be either * a content uri or a file uri * @param cid the id of the secure container that should * be used for creating a secure container into which the resource @@ -101,13 +101,13 @@ public class DefaultContainerService extends IntentService { */ public String copyResourceToContainer(final Uri packageURI, final String cid, final String key, final String resFileName, final String publicResFileName, - boolean isExternal, boolean isForwardLocked) { + boolean isExternal, boolean isForwardLocked, String abiOverride) { if (packageURI == null || cid == null) { return null; } return copyResourceInner(packageURI, cid, key, resFileName, publicResFileName, - isExternal, isForwardLocked); + isExternal, isForwardLocked, abiOverride); } /** @@ -153,13 +153,12 @@ public class DefaultContainerService extends IntentService { /** * Determine the recommended install location for package * specified by file uri location. - * @param fileUri the uri of resource to be copied. Should be a - * file uri + * * @return Returns PackageInfoLite object containing * the package info and recommended app location. */ public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags, - long threshold) { + long threshold, String abiOverride) { PackageInfoLite ret = new PackageInfoLite(); if (packagePath == null) { @@ -191,7 +190,7 @@ public class DefaultContainerService extends IntentService { ret.verifiers = pkg.verifiers; ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, - packagePath, flags, threshold); + packagePath, flags, threshold, abiOverride); return ret; } @@ -208,11 +207,11 @@ public class DefaultContainerService extends IntentService { } @Override - public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked) - throws RemoteException { + public boolean checkExternalFreeStorage(Uri packageUri, boolean isForwardLocked, + String abiOverride) throws RemoteException { final File apkFile = new File(packageUri.getPath()); try { - return isUnderExternalThreshold(apkFile, isForwardLocked); + return isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { return true; } @@ -265,11 +264,11 @@ public class DefaultContainerService extends IntentService { } @Override - public long calculateInstalledSize(String packagePath, boolean isForwardLocked) - throws RemoteException { + public long calculateInstalledSize(String packagePath, boolean isForwardLocked, + String abiOverride) throws RemoteException { final File packageFile = new File(packagePath); try { - return calculateContainerSize(packageFile, isForwardLocked) * 1024 * 1024; + return calculateContainerSize(packageFile, isForwardLocked, abiOverride) * 1024 * 1024; } catch (IOException e) { /* * Okay, something failed, so let's just estimate it to be 2x @@ -328,7 +327,8 @@ public class DefaultContainerService extends IntentService { } private String copyResourceInner(Uri packageURI, String newCid, String key, String resFileName, - String publicResFileName, boolean isExternal, boolean isForwardLocked) { + String publicResFileName, boolean isExternal, boolean isForwardLocked, + String abiOverride) { if (isExternal) { // Make sure the sdcard is mounted. @@ -343,7 +343,22 @@ public class DefaultContainerService extends IntentService { String codePath = packageURI.getPath(); File codeFile = new File(codePath); NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + String[] abiList = Build.SUPPORTED_ABIS; + if (abiOverride != null) { + abiList = new String[] { abiOverride }; + } else { + try { + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + } catch (IOException ioe) { + Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath()); + return null; + } + } + + final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); // Calculate size of container needed to hold base APK. final int sizeMb; @@ -414,7 +429,7 @@ public class DefaultContainerService extends IntentService { int ret = PackageManager.INSTALL_SUCCEEDED; if (abi >= 0) { ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - sharedLibraryDir, Build.SUPPORTED_ABIS[abi]); + sharedLibraryDir, abiList[abi]); } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) { ret = abi; } @@ -672,7 +687,7 @@ public class DefaultContainerService extends IntentService { private static final int PREFER_EXTERNAL = 2; private int recommendAppInstallLocation(int installLocation, String archiveFilePath, int flags, - long threshold) { + long threshold, String abiOverride) { int prefer; boolean checkBoth = false; @@ -741,7 +756,7 @@ public class DefaultContainerService extends IntentService { boolean fitsOnSd = false; if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) { try { - fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked); + fitsOnSd = isUnderExternalThreshold(apkFile, isForwardLocked, abiOverride); } catch (IOException e) { return PackageHelper.RECOMMEND_FAILED_INVALID_URI; } @@ -812,13 +827,13 @@ public class DefaultContainerService extends IntentService { * @return true if file fits * @throws IOException when file does not exist */ - private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked) + private boolean isUnderExternalThreshold(File apkFile, boolean isForwardLocked, String abiOverride) throws IOException { if (Environment.isExternalStorageEmulated()) { return false; } - final int sizeMb = calculateContainerSize(apkFile, isForwardLocked); + final int sizeMb = calculateContainerSize(apkFile, isForwardLocked, abiOverride); final int availSdMb; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { @@ -832,9 +847,11 @@ public class DefaultContainerService extends IntentService { return availSdMb > sizeMb; } - private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException { + private int calculateContainerSize(File apkFile, boolean forwardLocked, + String abiOverride) throws IOException { NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, + (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS); try { return calculateContainerSize(handle, apkFile, abi, forwardLocked); diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk index 1be44f9dd079..96ed2e773884 100644 --- a/packages/Keyguard/Android.mk +++ b/packages/Keyguard/Android.mk @@ -16,8 +16,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) \ - $(call all-proto-files-under,src) +LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-subdir-Iaidl-files) LOCAL_MODULE := Keyguard @@ -27,9 +26,6 @@ LOCAL_PRIVILEGED_MODULE := true LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_PROTOC_OPTIMIZE_TYPE := nano -LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors - LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java b/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java deleted file mode 100644 index 20af2f125c17..000000000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/KeyguardAnalytics.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import com.google.protobuf.nano.CodedOutputByteBufferNano; -import com.google.protobuf.nano.MessageNano; - -import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.AsyncTask; -import android.util.Log; -import android.view.MotionEvent; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * Tracks sessions, touch and sensor events in Keyguard. - * - * A session starts when the user is presented with the Keyguard and ends when the Keyguard is no - * longer visible to the user. - */ -public class KeyguardAnalytics implements SensorEventListener { - - private static final boolean DEBUG = false; - private static final String TAG = "KeyguardAnalytics"; - private static final long TIMEOUT_MILLIS = 11000; // 11 seconds. - - private static final int[] SENSORS = new int[] { - Sensor.TYPE_ACCELEROMETER, - Sensor.TYPE_GYROSCOPE, - Sensor.TYPE_PROXIMITY, - Sensor.TYPE_LIGHT, - Sensor.TYPE_ROTATION_VECTOR, - }; - - private Session mCurrentSession = null; - // Err on the side of caution, so logging is not started after a crash even tough the screen - // is off. - private boolean mScreenOn = false; - private boolean mHidden = false; - - private final SensorManager mSensorManager; - private final SessionTypeAdapter mSessionTypeAdapter; - private final File mAnalyticsFile; - - public KeyguardAnalytics(Context context, SessionTypeAdapter sessionTypeAdapter, - File analyticsFile) { - mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - mSessionTypeAdapter = sessionTypeAdapter; - mAnalyticsFile = analyticsFile; - } - - public Callback getCallback() { - return mCallback; - } - - public interface Callback { - public void onShow(); - public void onHide(); - public void onScreenOn(); - public void onScreenOff(); - public boolean onTouchEvent(MotionEvent ev, int width, int height); - public void onSetOccluded(boolean hidden); - } - - public interface SessionTypeAdapter { - public int getSessionType(); - } - - private void sessionEntrypoint() { - if (mCurrentSession == null && mScreenOn && !mHidden) { - onSessionStart(); - } - } - - private void sessionExitpoint(int result) { - if (mCurrentSession != null) { - onSessionEnd(result); - } - } - - private void onSessionStart() { - int type = mSessionTypeAdapter.getSessionType(); - mCurrentSession = new Session(System.currentTimeMillis(), System.nanoTime(), type); - if (type == Session.TYPE_KEYGUARD_SECURE) { - mCurrentSession.setRedactTouchEvents(); - } - for (int sensorType : SENSORS) { - Sensor s = mSensorManager.getDefaultSensor(sensorType); - if (s != null) { - mSensorManager.registerListener(this, s, SensorManager.SENSOR_DELAY_GAME); - } - } - if (DEBUG) { - Log.d(TAG, "onSessionStart()"); - } - } - - private void onSessionEnd(int result) { - if (DEBUG) { - Log.d(TAG, String.format("onSessionEnd(success=%d)", result)); - } - mSensorManager.unregisterListener(this); - - Session session = mCurrentSession; - mCurrentSession = null; - - session.end(System.currentTimeMillis(), result); - queueSession(session); - } - - private void queueSession(final Session currentSession) { - if (DEBUG) { - Log.i(TAG, "Saving session."); - } - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - try { - byte[] b = writeDelimitedProto(currentSession.toProto()); - OutputStream os = new FileOutputStream(mAnalyticsFile, true /* append */); - if (DEBUG) { - Log.d(TAG, String.format("Serialized size: %d kB.", b.length / 1024)); - } - try { - os.write(b); - os.flush(); - } finally { - try { - os.close(); - } catch (IOException e) { - Log.e(TAG, "Exception while closing file", e); - } - } - } catch (IOException e) { - Log.e(TAG, "Exception while writing file", e); - } - return null; - } - - private byte[] writeDelimitedProto(MessageNano proto) - throws IOException { - byte[] result = new byte[CodedOutputByteBufferNano.computeMessageSizeNoTag(proto)]; - CodedOutputByteBufferNano ob = CodedOutputByteBufferNano.newInstance(result); - ob.writeMessageNoTag(proto); - ob.checkNoSpaceLeft(); - return result; - } - }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); - } - - @Override - public synchronized void onSensorChanged(SensorEvent event) { - if (false) { - Log.v(TAG, String.format( - "onSensorChanged(name=%s, values[0]=%f)", - event.sensor.getName(), event.values[0])); - } - if (mCurrentSession != null) { - mCurrentSession.addSensorEvent(event, System.nanoTime()); - enforceTimeout(); - } - } - - private void enforceTimeout() { - if (System.currentTimeMillis() - mCurrentSession.getStartTimestampMillis() - > TIMEOUT_MILLIS) { - onSessionEnd(Session.RESULT_UNKNOWN); - if (DEBUG) { - Log.i(TAG, "Analytics timed out."); - } - } - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - - private final Callback mCallback = new Callback() { - @Override - public void onShow() { - if (DEBUG) { - Log.d(TAG, "onShow()"); - } - synchronized (KeyguardAnalytics.this) { - sessionEntrypoint(); - } - } - - @Override - public void onHide() { - if (DEBUG) { - Log.d(TAG, "onHide()"); - } - synchronized (KeyguardAnalytics.this) { - sessionExitpoint(Session.RESULT_SUCCESS); - } - } - - @Override - public void onScreenOn() { - if (DEBUG) { - Log.d(TAG, "onScreenOn()"); - } - synchronized (KeyguardAnalytics.this) { - mScreenOn = true; - sessionEntrypoint(); - } - } - - @Override - public void onScreenOff() { - if (DEBUG) { - Log.d(TAG, "onScreenOff()"); - } - synchronized (KeyguardAnalytics.this) { - mScreenOn = false; - sessionExitpoint(Session.RESULT_FAILURE); - } - } - - @Override - public boolean onTouchEvent(MotionEvent ev, int width, int height) { - if (DEBUG) { - Log.v(TAG, "onTouchEvent(ev.action=" - + MotionEvent.actionToString(ev.getAction()) + ")"); - } - synchronized (KeyguardAnalytics.this) { - if (mCurrentSession != null) { - mCurrentSession.addMotionEvent(ev); - mCurrentSession.setTouchArea(width, height); - enforceTimeout(); - } - } - return true; - } - - @Override - public void onSetOccluded(boolean hidden) { - synchronized (KeyguardAnalytics.this) { - if (hidden != mHidden) { - if (DEBUG) { - Log.d(TAG, "onSetOccluded(" + hidden + ")"); - } - mHidden = hidden; - if (hidden) { - // Could have gone to camera on purpose / by falsing or an app could have - // launched on top of the lockscreen. - sessionExitpoint(Session.RESULT_UNKNOWN); - } else { - sessionEntrypoint(); - } - } - } - } - }; - -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java b/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java deleted file mode 100644 index e68f751dd390..000000000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/PointerTracker.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import android.graphics.RectF; -import android.util.FloatMath; -import android.util.SparseArray; -import android.view.MotionEvent; - -import java.util.HashMap; -import java.util.Map; - -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent.BoundingBox; - -/** - * Takes motion events and tracks the length and bounding box of each pointer gesture as well as - * the bounding box of the whole gesture. - */ -public class PointerTracker { - private SparseArray<Pointer> mPointerInfoMap = new SparseArray<Pointer>(); - private RectF mTotalBoundingBox = new RectF(); - - public void addMotionEvent(MotionEvent ev) { - if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { - float x = ev.getX(); - float y = ev.getY(); - mTotalBoundingBox.set(x, y, x, y); - } - for (int i = 0; i < ev.getPointerCount(); i++) { - int id = ev.getPointerId(i); - Pointer pointer = getPointer(id); - float x = ev.getX(i); - float y = ev.getY(i); - boolean down = ev.getActionMasked() == MotionEvent.ACTION_DOWN - || (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN - && ev.getActionIndex() == i); - pointer.addPoint(x, y, down); - mTotalBoundingBox.union(x, y); - } - } - - public float getPointerLength(int id) { - return getPointer(id).length; - } - - public BoundingBox getBoundingBox() { - return boundingBoxFromRect(mTotalBoundingBox); - } - - public BoundingBox getPointerBoundingBox(int id) { - return boundingBoxFromRect(getPointer(id).boundingBox); - } - - private BoundingBox boundingBoxFromRect(RectF f) { - BoundingBox bb = new BoundingBox(); - bb.setHeight(f.height()); - bb.setWidth(f.width()); - return bb; - } - - private Pointer getPointer(int id) { - Pointer p = mPointerInfoMap.get(id); - if (p == null) { - p = new Pointer(); - mPointerInfoMap.put(id, p); - } - return p; - } - - private static class Pointer { - public float length; - public final RectF boundingBox = new RectF(); - - private float mLastX; - private float mLastY; - - public void addPoint(float x, float y, boolean down) { - float deltaX; - float deltaY; - if (down) { - boundingBox.set(x, y, x, y); - length = 0f; - deltaX = 0; - deltaY = 0; - } else { - deltaX = x - mLastX; - deltaY = y - mLastY; - } - mLastX = x; - mLastY = y; - length += FloatMath.sqrt(deltaX * deltaX + deltaY * deltaY); - boundingBox.union(x, y); - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java b/packages/Keyguard/src/com/android/keyguard/analytics/Session.java deleted file mode 100644 index 05f91654cc57..000000000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/Session.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.analytics; - -import android.os.Build; -import android.util.Slog; -import android.view.MotionEvent; - -import java.util.ArrayList; - -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.SensorEvent; -import static com.android.keyguard.analytics.KeyguardAnalyticsProtos.Session.TouchEvent; - -/** - * Records data about one keyguard session. - * - * The recorded data contains start and end of the session, whether it unlocked the device - * successfully, sensor data and touch data. - * - * If the keyguard is secure, the recorded touch data will correlate or contain the user pattern or - * PIN. If this is not desired, the touch coordinates can be redacted before serialization. - */ -public class Session { - - private static final String TAG = "KeyguardAnalytics"; - private static final boolean DEBUG = false; - - /** - * The user has failed to unlock the device in this session. - */ - public static final int RESULT_FAILURE = KeyguardAnalyticsProtos.Session.FAILURE; - /** - * The user has succeeded in unlocking the device in this session. - */ - public static final int RESULT_SUCCESS = KeyguardAnalyticsProtos.Session.SUCCESS; - - /** - * It is unknown how the session with the keyguard ended. - */ - public static final int RESULT_UNKNOWN = KeyguardAnalyticsProtos.Session.UNKNOWN; - - /** - * This session took place on an insecure keyguard. - */ - public static final int TYPE_KEYGUARD_INSECURE - = KeyguardAnalyticsProtos.Session.KEYGUARD_INSECURE; - - /** - * This session took place on an secure keyguard. - */ - public static final int TYPE_KEYGUARD_SECURE - = KeyguardAnalyticsProtos.Session.KEYGUARD_SECURE; - - /** - * This session took place during a fake wake up of the device. - */ - public static final int TYPE_RANDOM_WAKEUP = KeyguardAnalyticsProtos.Session.RANDOM_WAKEUP; - - - private final PointerTracker mPointerTracker = new PointerTracker(); - - private final long mStartTimestampMillis; - private final long mStartSystemTimeNanos; - private final int mType; - - private boolean mRedactTouchEvents; - private ArrayList<TouchEvent> mMotionEvents = new ArrayList<TouchEvent>(200); - private ArrayList<SensorEvent> mSensorEvents = new ArrayList<SensorEvent>(600); - private int mTouchAreaHeight; - private int mTouchAreaWidth; - - private long mEndTimestampMillis; - private int mResult; - private boolean mEnded; - - public Session(long startTimestampMillis, long startSystemTimeNanos, int type) { - mStartTimestampMillis = startTimestampMillis; - mStartSystemTimeNanos = startSystemTimeNanos; - mType = type; - } - - public void end(long endTimestampMillis, int result) { - mEnded = true; - mEndTimestampMillis = endTimestampMillis; - mResult = result; - } - - public void addMotionEvent(MotionEvent motionEvent) { - if (mEnded) { - return; - } - mPointerTracker.addMotionEvent(motionEvent); - mMotionEvents.add(protoFromMotionEvent(motionEvent)); - } - - public void addSensorEvent(android.hardware.SensorEvent eventOrig, long systemTimeNanos) { - if (mEnded) { - return; - } - SensorEvent event = protoFromSensorEvent(eventOrig, systemTimeNanos); - mSensorEvents.add(event); - if (DEBUG) { - Slog.v(TAG, String.format("addSensorEvent(name=%s, values[0]=%f", - event.getType(), event.values[0])); - } - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("Session{"); - sb.append("mType=").append(mType); - sb.append(", mStartTimestampMillis=").append(mStartTimestampMillis); - sb.append(", mStartSystemTimeNanos=").append(mStartSystemTimeNanos); - sb.append(", mEndTimestampMillis=").append(mEndTimestampMillis); - sb.append(", mResult=").append(mResult); - sb.append(", mRedactTouchEvents=").append(mRedactTouchEvents); - sb.append(", mTouchAreaHeight=").append(mTouchAreaHeight); - sb.append(", mTouchAreaWidth=").append(mTouchAreaWidth); - sb.append(", mMotionEvents=[size=").append(mMotionEvents.size()).append("]"); - sb.append(", mSensorEvents=[size=").append(mSensorEvents.size()).append("]"); - sb.append('}'); - return sb.toString(); - } - - public KeyguardAnalyticsProtos.Session toProto() { - KeyguardAnalyticsProtos.Session proto = new KeyguardAnalyticsProtos.Session(); - proto.setStartTimestampMillis(mStartTimestampMillis); - proto.setDurationMillis(mEndTimestampMillis - mStartTimestampMillis); - proto.setBuild(Build.FINGERPRINT); - proto.setResult(mResult); - proto.sensorEvents = mSensorEvents.toArray(proto.sensorEvents); - proto.touchEvents = mMotionEvents.toArray(proto.touchEvents); - proto.setTouchAreaWidth(mTouchAreaWidth); - proto.setTouchAreaHeight(mTouchAreaHeight); - proto.setType(mType); - if (mRedactTouchEvents) { - redactTouchEvents(proto.touchEvents); - } - return proto; - } - - private void redactTouchEvents(TouchEvent[] touchEvents) { - for (int i = 0; i < touchEvents.length; i++) { - TouchEvent t = touchEvents[i]; - for (int j = 0; j < t.pointers.length; j++) { - TouchEvent.Pointer p = t.pointers[j]; - p.clearX(); - p.clearY(); - } - t.setRedacted(true); - } - } - - private SensorEvent protoFromSensorEvent(android.hardware.SensorEvent ev, long sysTimeNanos) { - SensorEvent proto = new SensorEvent(); - proto.setType(ev.sensor.getType()); - proto.setTimeOffsetNanos(sysTimeNanos - mStartSystemTimeNanos); - proto.setTimestamp(ev.timestamp); - proto.values = ev.values.clone(); - return proto; - } - - private TouchEvent protoFromMotionEvent(MotionEvent ev) { - int count = ev.getPointerCount(); - TouchEvent proto = new TouchEvent(); - proto.setTimeOffsetNanos(ev.getEventTimeNano() - mStartSystemTimeNanos); - proto.setAction(ev.getActionMasked()); - proto.setActionIndex(ev.getActionIndex()); - proto.pointers = new TouchEvent.Pointer[count]; - for (int i = 0; i < count; i++) { - TouchEvent.Pointer p = new TouchEvent.Pointer(); - p.setX(ev.getX(i)); - p.setY(ev.getY(i)); - p.setSize(ev.getSize(i)); - p.setPressure(ev.getPressure(i)); - p.setId(ev.getPointerId(i)); - proto.pointers[i] = p; - if ((ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP && ev.getActionIndex() == i) - || ev.getActionMasked() == MotionEvent.ACTION_UP) { - p.boundingBox = mPointerTracker.getPointerBoundingBox(p.getId()); - p.setLength(mPointerTracker.getPointerLength(p.getId())); - } - } - if (ev.getActionMasked() == MotionEvent.ACTION_UP) { - proto.boundingBox = mPointerTracker.getBoundingBox(); - } - return proto; - } - - /** - * Discards the x / y coordinates of the touch events on serialization. Retained are the - * size of the individual and overall bounding boxes and the length of each pointer's gesture. - */ - public void setRedactTouchEvents() { - mRedactTouchEvents = true; - } - - public void setTouchArea(int width, int height) { - mTouchAreaWidth = width; - mTouchAreaHeight = height; - } - - public long getStartTimestampMillis() { - return mStartTimestampMillis; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto b/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto deleted file mode 100644 index 68b15908b30f..000000000000 --- a/packages/Keyguard/src/com/android/keyguard/analytics/keyguard_analytics.proto +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -syntax = "proto2"; - -package keyguard; - -option java_package = "com.android.keyguard.analytics"; -option java_outer_classname = "KeyguardAnalyticsProtos"; - -message Session { - message TouchEvent { - message BoundingBox { - optional float width = 1; - optional float height = 2; - } - - enum Action { - // Keep in sync with MotionEvent. - DOWN = 0; - UP = 1; - MOVE = 2; - CANCEL = 3; - OUTSIDE = 4; - POINTER_DOWN = 5; - POINTER_UP = 6; - } - - message Pointer { - optional float x = 1; - optional float y = 2; - optional float size = 3; - optional float pressure = 4; - optional int32 id = 5; - optional float length = 6; - // Bounding box of the pointer. Only set on UP or POINTER_UP event of this pointer. - optional BoundingBox boundingBox = 7; - } - - optional uint64 timeOffsetNanos = 1; - optional Action action = 2; - optional int32 actionIndex = 3; - repeated Pointer pointers = 4; - /* If true, the the x / y coordinates of the touch events were redacted. Retained are the - size of the individual and overall bounding boxes and the length of each pointer's - gesture. */ - optional bool redacted = 5; - // Bounding box of the whole gesture. Only set on UP event. - optional BoundingBox boundingBox = 6; - } - - message SensorEvent { - enum Type { - ACCELEROMETER = 1; - GYROSCOPE = 4; - LIGHT = 5; - PROXIMITY = 8; - ROTATION_VECTOR = 11; - } - - optional Type type = 1; - optional uint64 timeOffsetNanos = 2; - repeated float values = 3; - optional uint64 timestamp = 4; - } - - enum Result { - FAILURE = 0; - SUCCESS = 1; - UNKNOWN = 2; - } - - enum Type { - KEYGUARD_INSECURE = 0; - KEYGUARD_SECURE = 1; - RANDOM_WAKEUP = 2; - } - - optional uint64 startTimestampMillis = 1; - optional uint64 durationMillis = 2; - optional string build = 3; - optional Result result = 4; - repeated TouchEvent touchEvents = 5; - repeated SensorEvent sensorEvents = 6; - - optional int32 touchAreaWidth = 9; - optional int32 touchAreaHeight = 10; - optional Type type = 11; -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index f7b4994c705b..4837a539346a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -59,8 +59,6 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.MultiUserAvatarCache; import com.android.keyguard.ViewMediatorCallback; -import com.android.keyguard.analytics.KeyguardAnalytics; -import com.android.keyguard.analytics.Session; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.ScrimController; @@ -70,7 +68,6 @@ import com.android.systemui.statusbar.phone.StatusBarWindowManager; import java.io.File; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; -import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter; /** @@ -117,7 +114,6 @@ import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapte public class KeyguardViewMediator extends SystemUI { private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; final static boolean DEBUG = false; - private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE; private final static boolean DBG_WAKE = false; private final static String TAG = "KeyguardViewMediator"; @@ -199,8 +195,6 @@ public class KeyguardViewMediator extends SystemUI { private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private KeyguardAnalytics mKeyguardAnalytics; - // these are protected by synchronized (this) /** @@ -469,22 +463,6 @@ public class KeyguardViewMediator extends SystemUI { mViewMediatorCallback, mLockPatternUtils); final ContentResolver cr = mContext.getContentResolver(); - if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() && - Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) { - mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() { - - @Override - public int getSessionType() { - return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust( - mLockPatternUtils.getCurrentUser()) - ? Session.TYPE_KEYGUARD_SECURE - : Session.TYPE_KEYGUARD_INSECURE; - } - }, new File(mContext.getCacheDir(), "keyguard_analytics.bin")); - } else { - mKeyguardAnalytics = null; - } - mScreenOn = mPM.isScreenOn(); mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0); @@ -585,9 +563,6 @@ public class KeyguardViewMediator extends SystemUI { } else { doKeyguardLocked(null); } - if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { - mKeyguardAnalytics.getCallback().onScreenOff(); - } } KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why); } @@ -830,9 +805,6 @@ public class KeyguardViewMediator extends SystemUI { updateActivityLockScreenState(); adjustStatusBarLocked(); } - if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) { - mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 994b3292e551..97aa993864ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; @@ -32,6 +33,10 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.ImageView; + +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; /** @@ -43,6 +48,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView"; + private static final Intent SECURE_CAMERA_INTENT = + new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) + .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + private static final Intent INSECURE_CAMERA_INTENT = + new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL); private ImageView mCameraImageView; @@ -51,6 +61,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private ActivityStarter mActivityStarter; private UnlockMethodCache mUnlockMethodCache; + private LockPatternUtils mLockPatternUtils; public KeyguardBottomAreaView(Context context) { super(context); @@ -72,10 +83,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override protected void onFinishInflate() { super.onFinishInflate(); + mLockPatternUtils = new LockPatternUtils(mContext); mCameraImageView = (ImageView) findViewById(R.id.camera_button); mPhoneImageView = (ImageView) findViewById(R.id.phone_button); mLockIcon = (ImageView) findViewById(R.id.lock_icon); - watchForDevicePolicyChanges(); + watchForCameraPolicyChanges(); watchForAccessibilityChanges(); updateCameraVisibility(); updatePhoneVisibility(); @@ -88,8 +100,19 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mActivityStarter = activityStarter; } + private Intent getCameraIntent() { + KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); + boolean currentUserHasTrust = updateMonitor.getUserHasTrust( + mLockPatternUtils.getCurrentUser()); + return mLockPatternUtils.isSecure() && !currentUserHasTrust + ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; + } + private void updateCameraVisibility() { - boolean visible = !isCameraDisabledByDpm(); + ResolveInfo resolved = mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), + PackageManager.MATCH_DEFAULT_ONLY, + mLockPatternUtils.getCurrentUser()); + boolean visible = !isCameraDisabledByDpm() && resolved != null; mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE); } @@ -122,19 +145,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL return false; } - private void watchForDevicePolicyChanges() { + private void watchForCameraPolicyChanges() { final IntentFilter filter = new IntentFilter(); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - getContext().registerReceiver(new BroadcastReceiver() { - public void onReceive(Context context, Intent intent) { - post(new Runnable() { - @Override - public void run() { - updateCameraVisibility(); - } - }); - } - }, filter); + getContext().registerReceiverAsUser(mDevicePolicyReceiver, + UserHandle.ALL, filter, null, null); + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); } private void watchForAccessibilityChanges() { @@ -171,9 +187,12 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } public void launchCamera() { - mContext.startActivityAsUser( - new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE), - UserHandle.CURRENT); + Intent intent = getCameraIntent(); + if (intent == SECURE_CAMERA_INTENT) { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } else { + mActivityStarter.startActivity(intent); + } } public void launchPhone() { @@ -186,6 +205,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL super.onVisibilityChanged(changedView, visibility); if (changedView == this && visibility == VISIBLE) { updateTrust(); + updateCameraVisibility(); } } @@ -214,5 +234,25 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void onMethodSecureChanged(boolean methodSecure) { updateTrust(); + updateCameraVisibility(); } + + private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + post(new Runnable() { + @Override + public void run() { + updateCameraVisibility(); + } + }); + } + }; + + private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onUserSwitchComplete(int userId) { + updateCameraVisibility(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 1344703287ad..7c87580ae9b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -95,7 +95,6 @@ public class QSTileHost implements QSTile.Host { mTiles.add(new LocationTile(this)); mTiles.add(new CastTile(this)); mTiles.add(new HotspotTile(this)); - mTiles.add(new BugreportTile(this)); mUserTracker = new CurrentUserTracker(mContext) { @Override diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index 762d3dfca55c..0c16b78ca441 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -35,6 +35,7 @@ import android.database.ContentObserver; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.ConnectivityManager; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -51,6 +52,7 @@ import android.service.dreams.IDreamManager; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.TypedValue; @@ -345,8 +347,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac } private Action getBugReportAction() { - return new SinglePressAction(com.android.internal.R.drawable.stat_sys_adb, - R.string.global_action_bug_report) { + return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport, + R.string.bugreport_title) { public void onPress() { AlertDialog.Builder builder = new AlertDialog.Builder(mContext); @@ -383,6 +385,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac public boolean showBeforeProvisioning() { return false; } + + @Override + public String getStatus() { + return mContext.getString( + com.android.internal.R.string.bugreport_status, + Build.VERSION.RELEASE, + Build.ID); + } }; } @@ -640,6 +650,10 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac return true; } + public String getStatus() { + return null; + } + abstract public void onPress(); public View create( @@ -649,7 +663,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac ImageView icon = (ImageView) v.findViewById(R.id.icon); TextView messageView = (TextView) v.findViewById(R.id.message); - v.findViewById(R.id.status).setVisibility(View.GONE); + TextView statusView = (TextView) v.findViewById(R.id.status); + final String status = getStatus(); + if (!TextUtils.isEmpty(status)) { + statusView.setText(status); + } else { + statusView.setVisibility(View.GONE); + } if (mIcon != null) { icon.setImageDrawable(mIcon); icon.setScaleType(ScaleType.CENTER_CROP); diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 0082b1e669c5..14c15a7cc4c4 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -26,6 +26,7 @@ import android.app.PendingIntent; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupTransport; import android.app.backup.FullBackup; import android.app.backup.RestoreSet; import android.app.backup.IBackupManager; @@ -82,7 +83,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.StringBuilderPrinter; -import com.android.internal.backup.BackupConstants; import com.android.internal.backup.IBackupTransport; import com.android.internal.backup.IObbBackupService; import com.android.server.AppWidgetBackupBridge; @@ -2098,7 +2098,7 @@ public class BackupManagerService extends IBackupManager.Stub { } mAgentBinder = null; - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; // Sanity check: if the queue is empty we have no work to do. if (mOriginalQueue.isEmpty()) { @@ -2121,14 +2121,14 @@ public class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(EventLogTags.BACKUP_START, transportName); // If we haven't stored package manager metadata yet, we must init the transport. - if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) { + if (mStatus == BackupTransport.TRANSPORT_OK && pmState.length() <= 0) { Slog.i(TAG, "Initializing (wiping) backup state and transport storage"); addBackupTrace("initializing transport " + transportName); resetBackupState(mStateDir); // Just to make sure. mStatus = mTransport.initializeDevice(); addBackupTrace("transport.initializeDevice() == " + mStatus); - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); } else { EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)"); @@ -2141,7 +2141,7 @@ public class BackupManagerService extends IBackupManager.Stub { // directly and use a synthetic BackupRequest. We always run this pass // because it's cheap and this way we guarantee that we don't get out of // step even if we're selecting among various transports at run time. - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( mPackageManager, allAgentPackages()); mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL, @@ -2149,7 +2149,7 @@ public class BackupManagerService extends IBackupManager.Stub { addBackupTrace("PMBA invoke: " + mStatus); } - if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { // The backend reports that our dataset has been wiped. Note this in // the event log; the no-success code below will reset the backup // state as well. @@ -2158,13 +2158,13 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (Exception e) { Slog.e(TAG, "Error in backup thread", e); addBackupTrace("Exception in backup thread: " + e); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; } finally { // If we've succeeded so far, invokeAgentForBackup() will have run the PM // metadata and its completion/timeout callback will continue the state // machine chain. If it failed that won't happen; we handle that now. addBackupTrace("exiting prelim: " + mStatus); - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { // if things went wrong at this point, we need to // restage everything and try again later. resetBackupState(mStateDir); // Just to make sure. @@ -2176,7 +2176,7 @@ public class BackupManagerService extends IBackupManager.Stub { // Transport has been initialized and the PM metadata submitted successfully // if that was warranted. Now we process the single next thing in the queue. void invokeNextAgent() { - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; addBackupTrace("invoke q=" + mQueue.size()); // Sanity check that we have work to do. If not, skip to the end where @@ -2236,39 +2236,39 @@ public class BackupManagerService extends IBackupManager.Stub { // done here as long as we're successful so far. } else { // Timeout waiting for the agent - mStatus = BackupConstants.AGENT_ERROR; + mStatus = BackupTransport.AGENT_ERROR; } } catch (SecurityException ex) { // Try for the next one. Slog.d(TAG, "error in bind/backup", ex); - mStatus = BackupConstants.AGENT_ERROR; + mStatus = BackupTransport.AGENT_ERROR; addBackupTrace("agent SE"); } } catch (NameNotFoundException e) { Slog.d(TAG, "Package does not exist; skipping"); addBackupTrace("no such package"); - mStatus = BackupConstants.AGENT_UNKNOWN; + mStatus = BackupTransport.AGENT_UNKNOWN; } finally { mWakelock.setWorkSource(null); // If there was an agent error, no timeout/completion handling will occur. // That means we need to direct to the next state ourselves. - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { BackupState nextState = BackupState.RUNNING_QUEUE; mAgentBinder = null; // An agent-level failure means we reenqueue this one agent for // a later retry, but otherwise proceed normally. - if (mStatus == BackupConstants.AGENT_ERROR) { + if (mStatus == BackupTransport.AGENT_ERROR) { if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName + " - restaging"); dataChangedImpl(request.packageName); - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; if (mQueue.isEmpty()) nextState = BackupState.FINAL; - } else if (mStatus == BackupConstants.AGENT_UNKNOWN) { + } else if (mStatus == BackupTransport.AGENT_UNKNOWN) { // Failed lookup of the app, so we couldn't bring up an agent, but // we're otherwise fine. Just drop it and go on to the next as usual. - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; } else { // Transport-level failure means we reenqueue everything revertAndEndBackup(); @@ -2297,7 +2297,7 @@ public class BackupManagerService extends IBackupManager.Stub { // If everything actually went through and this is the first time we've // done a backup, we can now record what the current backup dataset token // is. - if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) { + if ((mCurrentToken == 0) && (mStatus == BackupTransport.TRANSPORT_OK)) { addBackupTrace("success; recording token"); try { mCurrentToken = mTransport.getCurrentRestoreSet(); @@ -2314,7 +2314,7 @@ public class BackupManagerService extends IBackupManager.Stub { // state machine sequence and the wakelock is refcounted. synchronized (mQueueLock) { mBackupRunning = false; - if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) { + if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) { // Make sure we back up everything and perform the one-time init clearMetadata(); if (DEBUG) Slog.d(TAG, "Server requires init; rerunning"); @@ -2395,7 +2395,7 @@ public class BackupManagerService extends IBackupManager.Stub { EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString()); agentErrorCleanup(); - return BackupConstants.AGENT_ERROR; + return BackupTransport.AGENT_ERROR; } // At this point the agent is off and running. The next thing to happen will @@ -2403,7 +2403,7 @@ public class BackupManagerService extends IBackupManager.Stub { // for transport, or a timeout. Either way the next phase will happen in // response to the TimeoutHandler interface callbacks. addBackupTrace("invoke success"); - return BackupConstants.TRANSPORT_OK; + return BackupTransport.TRANSPORT_OK; } public void failAgent(IBackupAgent agent, String message) { @@ -2484,11 +2484,11 @@ public class BackupManagerService extends IBackupManager.Stub { addBackupTrace("operation complete"); ParcelFileDescriptor backupData = null; - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; try { int size = (int) mBackupDataName.length(); if (size > 0) { - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { backupData = ParcelFileDescriptor.open(mBackupDataName, ParcelFileDescriptor.MODE_READ_ONLY); addBackupTrace("sending data to transport"); @@ -2501,7 +2501,7 @@ public class BackupManagerService extends IBackupManager.Stub { // renaming *all* the output state files (see below) until that happens. addBackupTrace("data delivered: " + mStatus); - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { addBackupTrace("finishing op on transport"); mStatus = mTransport.finishBackup(); addBackupTrace("finished: " + mStatus); @@ -2514,7 +2514,7 @@ public class BackupManagerService extends IBackupManager.Stub { // After successful transport, delete the now-stale data // and juggle the files so that next time we supply the agent // with the new state file it just created. - if (mStatus == BackupConstants.TRANSPORT_OK) { + if (mStatus == BackupTransport.TRANSPORT_OK) { mBackupDataName.delete(); mNewStateName.renameTo(mSavedStateName); EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size); @@ -2525,7 +2525,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (Exception e) { Slog.e(TAG, "Transport error backing up " + pkgName, e); EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; } finally { try { if (backupData != null) backupData.close(); } catch (IOException e) {} } @@ -2533,7 +2533,7 @@ public class BackupManagerService extends IBackupManager.Stub { // If we encountered an error here it's a transport-level failure. That // means we need to halt everything and reschedule everything for next time. final BackupState nextState; - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { revertAndEndBackup(); nextState = BackupState.FINAL; } else { @@ -4847,7 +4847,7 @@ public class BackupManagerService extends IBackupManager.Stub { mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT); // Assume error until we successfully init everything - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; try { // TODO: Log this before getAvailableRestoreSets, somehow @@ -4902,7 +4902,7 @@ public class BackupManagerService extends IBackupManager.Stub { return; } - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; executeNextState(RestoreState.DOWNLOAD_DATA); } @@ -4917,7 +4917,7 @@ public class BackupManagerService extends IBackupManager.Stub { try { mStatus = mTransport.startRestore(mToken, mRestorePackages.toArray(new PackageInfo[0])); - if (mStatus != BackupConstants.TRANSPORT_OK) { + if (mStatus != BackupTransport.TRANSPORT_OK) { Slog.e(TAG, "Error starting restore operation"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); executeNextState(RestoreState.FINAL); @@ -4926,7 +4926,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (RemoteException e) { Slog.e(TAG, "Error communicating with transport for restore"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); return; } @@ -4941,14 +4941,14 @@ public class BackupManagerService extends IBackupManager.Stub { if (packageName == null) { Slog.e(TAG, "Error getting first restore package"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); return; } else if (packageName.equals("")) { Slog.i(TAG, "No restore data available"); int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime); EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis); - mStatus = BackupConstants.TRANSPORT_OK; + mStatus = BackupTransport.TRANSPORT_OK; executeNextState(RestoreState.FINAL); return; } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { @@ -4979,7 +4979,7 @@ public class BackupManagerService extends IBackupManager.Stub { Slog.e(TAG, "No restore metadata available, so not restoring settings"); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL, "Package manager restore metadata missing"); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); executeNextState(RestoreState.FINAL); return; @@ -4987,7 +4987,7 @@ public class BackupManagerService extends IBackupManager.Stub { } catch (RemoteException e) { Slog.e(TAG, "Error communicating with transport for restore"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this); executeNextState(RestoreState.FINAL); return; @@ -5118,7 +5118,7 @@ public class BackupManagerService extends IBackupManager.Stub { } } catch (RemoteException e) { Slog.e(TAG, "Unable to fetch restore data from transport"); - mStatus = BackupConstants.TRANSPORT_ERROR; + mStatus = BackupTransport.TRANSPORT_ERROR; executeNextState(RestoreState.FINAL); } } @@ -5206,7 +5206,7 @@ public class BackupManagerService extends IBackupManager.Stub { Slog.e(TAG, "SElinux restorecon failed for " + downloadFile); } - if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) { + if (mTransport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) { // Transport-level failure, so we wind everything up and // terminate the restore operation. Slog.e(TAG, "Error getting restore data for " + packageName); @@ -5450,12 +5450,12 @@ public class BackupManagerService extends IBackupManager.Stub { long startRealtime = SystemClock.elapsedRealtime(); int status = transport.initializeDevice(); - if (status == BackupConstants.TRANSPORT_OK) { + if (status == BackupTransport.TRANSPORT_OK) { status = transport.finishBackup(); } // Okay, the wipe really happened. Clean up our local bookkeeping. - if (status == BackupConstants.TRANSPORT_OK) { + if (status == BackupTransport.TRANSPORT_OK) { Slog.i(TAG, "Device init successful"); int millis = (int) (SystemClock.elapsedRealtime() - startRealtime); EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d7a19add8f73..b2b421714058 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -38,7 +38,6 @@ import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.ConnectivityManager.TYPE_PROXY; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; -import static android.net.ConnectivityServiceProtocol.NetworkFactoryProtocol; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -80,6 +79,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; +import android.net.NetworkFactory; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkState; @@ -258,17 +258,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private NetworkStateTracker mNetTrackers[]; - /** - * Holds references to all NetworkAgentInfos claiming to support the legacy - * NetworkType. We used to have a static set of of NetworkStateTrackers - * for each network type. This is the new model. - * Supports synchronous inspection of state. - * These are built out at startup such that an unsupported network - * doesn't get an ArrayList instance, making this a tristate: - * unsupported, supported but not active and active. - */ - private ArrayList<NetworkAgentInfo> mNetworkAgentInfoForType[]; - /* Handles captive portal check on a network */ private CaptivePortalTracker mCaptivePortalTracker; @@ -516,6 +505,118 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int UID_UNUSED = -1; + /** + * Implements support for the legacy "one network per network type" model. + * + * We used to have a static array of NetworkStateTrackers, one for each + * network type, but that doesn't work any more now that we can have, + * for example, more that one wifi network. This class stores all the + * NetworkAgentInfo objects that support a given type, but the legacy + * API will only see the first one. + * + * It serves two main purposes: + * + * 1. Provide information about "the network for a given type" (since this + * API only supports one). + * 2. Send legacy connectivity change broadcasts. Broadcasts are sent if + * the first network for a given type changes, or if the default network + * changes. + */ + private class LegacyTypeTracker { + /** + * Array of lists, one per legacy network type (e.g., TYPE_MOBILE_MMS). + * Each list holds references to all NetworkAgentInfos that are used to + * satisfy requests for that network type. + * + * This array is built out at startup such that an unsupported network + * doesn't get an ArrayList instance, making this a tristate: + * unsupported, supported but not active and active. + * + * The actual lists are populated when we scan the network types that + * are supported on this device. + */ + private ArrayList<NetworkAgentInfo> mTypeLists[]; + + public LegacyTypeTracker() { + mTypeLists = (ArrayList<NetworkAgentInfo>[]) + new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; + } + + public void addSupportedType(int type) { + if (mTypeLists[type] != null) { + throw new IllegalStateException( + "legacy list for type " + type + "already initialized"); + } + mTypeLists[type] = new ArrayList<NetworkAgentInfo>(); + } + + private boolean isDefaultNetwork(NetworkAgentInfo nai) { + return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai; + } + + public boolean isTypeSupported(int type) { + return isNetworkTypeValid(type) && mTypeLists[type] != null; + } + + public NetworkAgentInfo getNetworkForType(int type) { + if (isTypeSupported(type) && !mTypeLists[type].isEmpty()) { + return mTypeLists[type].get(0); + } else { + return null; + } + } + + public void add(int type, NetworkAgentInfo nai) { + if (!isTypeSupported(type)) { + return; // Invalid network type. + } + if (VDBG) log("Adding agent " + nai + " for legacy network type " + type); + + ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + if (list.contains(nai)) { + loge("Attempting to register duplicate agent for type " + type + ": " + nai); + return; + } + + if (list.isEmpty() || isDefaultNetwork(nai)) { + if (VDBG) log("Sending connected broadcast for type " + type + + "isDefaultNetwork=" + isDefaultNetwork(nai)); + sendLegacyNetworkBroadcast(nai, true, type); + } + list.add(nai); + } + + public void remove(NetworkAgentInfo nai) { + if (VDBG) log("Removing agent " + nai); + for (int type = 0; type < mTypeLists.length; type++) { + ArrayList<NetworkAgentInfo> list = mTypeLists[type]; + if (list == null || list.isEmpty()) { + continue; + } + + boolean wasFirstNetwork = false; + if (list.get(0).equals(nai)) { + // This network was the first in the list. Send broadcast. + wasFirstNetwork = true; + } + list.remove(nai); + + if (wasFirstNetwork || isDefaultNetwork(nai)) { + if (VDBG) log("Sending disconnected broadcast for type " + type + + "isDefaultNetwork=" + isDefaultNetwork(nai)); + sendLegacyNetworkBroadcast(nai, false, type); + } + + if (!list.isEmpty() && wasFirstNetwork) { + if (VDBG) log("Other network available for type " + type + + ", sending connected broadcast"); + sendLegacyNetworkBroadcast(list.get(0), false, type); + } + } + } + } + private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(); + public ConnectivityService(Context context, INetworkManagementService netd, INetworkStatsService statsService, INetworkPolicyManager policyManager) { // Currently, omitting a NetworkFactory will create one internally @@ -531,7 +632,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - mDefaultRequest = new NetworkRequest(netCap, true, nextNetworkRequestId()); + mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST); mNetworkRequests.put(mDefaultRequest, nri); @@ -587,9 +688,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_networkTransitionTimeout); - mNetworkAgentInfoForType = (ArrayList<NetworkAgentInfo>[]) - new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1]; - mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; mCurrentLinkProperties = new LinkProperties[ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -644,7 +742,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { "radio " + n.radio + " in network type " + n.type); continue; } - mNetworkAgentInfoForType[n.type] = new ArrayList<NetworkAgentInfo>(); + mLegacyTypeTracker.addSupportedType(n.type); mNetConfigs[n.type] = n; mNetworksDefined++; @@ -2843,7 +2941,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private int getRestoreDefaultNetworkDelay(int networkType) { + @Override + public int getRestoreDefaultNetworkDelay(int networkType) { String restoreDefaultNetworkDelayStr = SystemProperties.get( NETWORK_RESTORE_DELAY_PROP_NAME); if(restoreDefaultNetworkDelayStr != null && @@ -2994,6 +3093,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { updateNetworkInfo(nai, info); break; } + case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { + NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); + if (nai == null) { + loge("EVENT_NETWORK_SCORE_CHANGED from unknown NetworkAgent"); + break; + } + Integer score = (Integer) msg.obj; + updateNetworkScore(nai, score); + break; + } case NetworkMonitor.EVENT_NETWORK_VALIDATED: { NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj; handleConnectionValidated(nai); @@ -3098,7 +3207,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.isRequest == false) continue; NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); - ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, + ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, (nai != null ? nai.currentScore : 0), 0, nri.request); } } else { @@ -3114,11 +3223,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { loge("Error connecting NetworkAgent"); NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo); - try { - mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); - } catch (NullPointerException e) {} if (nai != null) { mNetworkForNetId.remove(nai.network.netId); + mLegacyTypeTracker.remove(nai); } } } @@ -3137,14 +3244,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { } catch (Exception e) { loge("Exception removing network: " + e); } + // TODO - if we move the logic to the network agent (have them disconnect + // because they lost all their requests or because their score isn't good) + // then they would disconnect organically, report their new state and then + // disconnect the channel. + if (nai.networkInfo.isConnected()) { + nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, + null, null); + } notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); mNetworkAgentInfos.remove(msg.replyTo); updateClat(null, nai.linkProperties, nai); - try { - mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai); - } catch (NullPointerException e) {} - + mLegacyTypeTracker.remove(nai); mNetworkForNetId.remove(nai.network.netId); // Since we've lost the network, go through all the requests that // it was satisfying and see if any other factory can satisfy them. @@ -3154,7 +3266,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); if (VDBG) { log(" checking request " + request + ", currentNetwork = " + - currentNetwork != null ? currentNetwork.name() : "null"); + (currentNetwork != null ? currentNetwork.name() : "null")); } if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { mNetworkForRequestId.remove(request.requestId); @@ -3203,7 +3315,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { } if (bestNetwork != null) { if (VDBG) log("using " + bestNetwork.name()); - bestNetwork.networkRequests.put(nri.request.requestId, nri.request); + bestNetwork.addRequest(nri.request); + int legacyType = nri.request.legacyType; + if (legacyType != TYPE_NONE) { + mLegacyTypeTracker.add(legacyType, bestNetwork); + } notifyNetworkCallback(bestNetwork, nri); score = bestNetwork.currentScore; } @@ -3211,7 +3327,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) { if (DBG) log("sending new NetworkRequest to factories"); for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, + 0, nri.request); } } } @@ -3233,7 +3350,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (nri.isRequest) { for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, + nri.request); } if (affectedNetwork != null) { @@ -5279,7 +5397,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutSec, IBinder binder) { + Messenger messenger, int timeoutSec, IBinder binder, int legacyType) { if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { enforceConnectivityInternalPermission(); @@ -5291,7 +5409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { throw new IllegalArgumentException("Bad timeout specified"); } NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( - networkCapabilities), false, nextNetworkRequestId()); + networkCapabilities), legacyType, nextNetworkRequestId()); if (DBG) log("requestNetwork for " + networkRequest); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, NetworkRequestInfo.REQUEST); @@ -5317,7 +5435,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { enforceAccessPermission(); NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( - networkCapabilities), false, nextNetworkRequestId()); + networkCapabilities), TYPE_NONE, nextNetworkRequestId()); if (DBG) log("listenForNetwork for " + networkRequest); NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder, NetworkRequestInfo.LISTEN); @@ -5392,18 +5510,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(), new NetworkInfo(networkInfo), new LinkProperties(linkProperties), new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler); - + if (VDBG) log("registerNetworkAgent " + nai); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); } private void handleRegisterNetworkAgent(NetworkAgentInfo na) { if (VDBG) log("Got NetworkAgent Messenger"); mNetworkAgentInfos.put(na.messenger, na); - try { - mNetworkAgentInfoForType[na.networkInfo.getType()].add(na); - } catch (NullPointerException e) { - loge("registered NetworkAgent for unsupported type: " + na); - } mNetworkForNetId.put(na.network.netId, na); na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger); NetworkInfo networkInfo = na.networkInfo; @@ -5439,7 +5552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mClat.stopClat(); } // If the link requires clat to be running, then start the daemon now. - if (newLp != null && na.networkInfo.isConnected()) { + if (na.networkInfo.isConnected()) { mClat.startClat(na); } else { mClat.stopClat(); @@ -5555,7 +5668,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) { if (VDBG) log("sending new Min Network Score(" + score + "): " + networkRequest.toString()); for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) { - nfi.asyncChannel.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, networkRequest); + nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0, + networkRequest); } } @@ -5658,7 +5772,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (VDBG) log(" accepting network in place of null"); } mNetworkForRequestId.put(nri.request.requestId, newNetwork); - newNetwork.networkRequests.put(nri.request.requestId, nri.request); + newNetwork.addRequest(nri.request); + int legacyType = nri.request.legacyType; + if (legacyType != TYPE_NONE) { + mLegacyTypeTracker.add(legacyType, newNetwork); + } keep = true; // TODO - this could get expensive if we have alot of requests for this // network. Think about if there is a way to reduce this. Push @@ -5672,6 +5790,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else { setDefaultDnsSystemProperties(new ArrayList<InetAddress>()); } + mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); } } } @@ -5792,6 +5911,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } + private void updateNetworkScore(NetworkAgentInfo nai, Integer scoreInteger) { + int score = scoreInteger.intValue(); + // TODO + } + // notify only this one new request of the current state protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) { int notifyType = ConnectivityManager.CALLBACK_AVAILABLE; @@ -5801,93 +5925,88 @@ public class ConnectivityService extends IConnectivityManager.Stub { // } else if (nai.networkMonitor.isEvaluating()) { // notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType); // } - if (nri.request.needsBroadcasts) { - // TODO -// sendNetworkBroadcast(nai, notifyType); - } callCallbackForRequest(nri, nai, notifyType); } + private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) { + if (connected) { + NetworkInfo info = new NetworkInfo(nai.networkInfo); + info.setType(type); + sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay()); + } else { + NetworkInfo info = new NetworkInfo(nai.networkInfo); + info.setType(type); + Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); + if (info.isFailover()) { + intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); + nai.networkInfo.setFailover(false); + } + if (info.getReason() != null) { + intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); + } + if (info.getExtraInfo() != null) { + intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); + } + NetworkAgentInfo newDefaultAgent = null; + if (nai.networkRequests.get(mDefaultRequest.requestId) != null) { + newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); + if (newDefaultAgent != null) { + intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, + newDefaultAgent.networkInfo); + } else { + intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); + } + } + intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, + mDefaultInetConditionPublished); + final Intent immediateIntent = new Intent(intent); + immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); + sendStickyBroadcast(immediateIntent); + sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); + if (newDefaultAgent != null) { + sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo, + getConnectivityChangeDelay()); + } + } + } + protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) { if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name()); - boolean needsBroadcasts = false; for (int i = 0; i < networkAgent.networkRequests.size(); i++) { NetworkRequest nr = networkAgent.networkRequests.valueAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); if (VDBG) log(" sending notification for " + nr); - if (nr.needsBroadcasts) needsBroadcasts = true; callCallbackForRequest(nri, networkAgent, notifyType); } - if (needsBroadcasts) { - if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) { - sendConnectedBroadcastDelayed(networkAgent.networkInfo, - getConnectivityChangeDelay()); - } else if (notifyType == ConnectivityManager.CALLBACK_LOST) { - NetworkInfo info = new NetworkInfo(networkAgent.networkInfo); - Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType()); - if (info.isFailover()) { - intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); - networkAgent.networkInfo.setFailover(false); - } - if (info.getReason() != null) { - intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason()); - } - if (info.getExtraInfo() != null) { - intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); - } - NetworkAgentInfo newDefaultAgent = null; - if (networkAgent.networkRequests.get(mDefaultRequest.requestId) != null) { - newDefaultAgent = mNetworkForRequestId.get(mDefaultRequest.requestId); - if (newDefaultAgent != null) { - intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, - newDefaultAgent.networkInfo); - } else { - intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); - } - } - intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, - mDefaultInetConditionPublished); - final Intent immediateIntent = new Intent(intent); - immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); - sendStickyBroadcast(immediateIntent); - sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); - if (newDefaultAgent != null) { - sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo, - getConnectivityChangeDelay()); - } - } - } } private LinkProperties getLinkPropertiesForTypeInternal(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new LinkProperties(list.get(0).linkProperties); - } catch (IndexOutOfBoundsException e) { - return new LinkProperties(); - } + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + return (nai != null) ? + new LinkProperties(nai.linkProperties) : + new LinkProperties(); } private NetworkInfo getNetworkInfoForType(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new NetworkInfo(list.get(0).networkInfo); - } catch (IndexOutOfBoundsException e) { - return new NetworkInfo(networkType, 0, "Unknown", ""); + if (!mLegacyTypeTracker.isTypeSupported(networkType)) + return null; + + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + if (nai != null) { + NetworkInfo result = new NetworkInfo(nai.networkInfo); + result.setType(networkType); + return result; + } else { + return new NetworkInfo(networkType, 0, "Unknown", ""); } } private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) { - ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType]; - if (list == null) return null; - try { - return new NetworkCapabilities(list.get(0).networkCapabilities); - } catch (IndexOutOfBoundsException e) { - return new NetworkCapabilities(); - } + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + return (nai != null) ? + new NetworkCapabilities(nai.networkCapabilities) : + new NetworkCapabilities(); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 61ba6e062d18..aede7974f127 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2978,6 +2978,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } buf.append("}"); + if (requiredAbi != null) { + buf.append(" abi="); + buf.append(requiredAbi); + } Slog.i(TAG, buf.toString()); app.setPid(startResult.pid); app.usingWrapper = startResult.usingWrapper; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 16ad1533645e..ba12374acef4 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -2537,7 +2537,9 @@ final class ActivityStack { + " who=" + r.resultWho + " req=" + r.requestCode + " res=" + resultCode + " data=" + resultData); if (resultTo.userId != r.userId) { - resultData.prepareToLeaveUser(r.userId); + if (resultData != null) { + resultData.prepareToLeaveUser(r.userId); + } } if (r.info.applicationInfo.uid > 0) { mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid, diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 8102591ea932..b03c247e80dc 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -45,7 +45,6 @@ public class NetworkAgentInfo { public int currentScore; public final NetworkMonitor networkMonitor; - // The list of NetworkRequests being satisfied by this Network. public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>(); public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>(); @@ -66,6 +65,10 @@ public class NetworkAgentInfo { networkMonitor = new NetworkMonitor(context, handler, this); } + public void addRequest(NetworkRequest networkRequest) { + networkRequests.put(networkRequest.requestId, networkRequest); + } + public String toString() { return "NetworkAgentInfo{ ni{" + networkInfo + "} network{" + network + "} lp{" + diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index d0b716d32b5d..5141d16a2a70 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -505,7 +505,7 @@ final class HdmiCecController { // Reply <Feature Abort> to initiator (source) for all requests. HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand( sourceAddress, message.getSource(), message.getOpcode(), - HdmiCecMessageBuilder.ABORT_REFUSED); + HdmiConstants.ABORT_REFUSED); sendCommand(cecMessage, null); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 9a76734943d6..6c2be34bfd78 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -26,15 +26,6 @@ import java.util.Arrays; * A helper class to build {@link HdmiCecMessage} from various cec commands. */ public class HdmiCecMessageBuilder { - // TODO: move these values to HdmiCec.java once make it internal constant class. - // CEC's ABORT reason values. - static final int ABORT_UNRECOGNIZED_MODE = 0; - static final int ABORT_NOT_IN_CORRECT_MODE = 1; - static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; - static final int ABORT_INVALID_OPERAND = 3; - static final int ABORT_REFUSED = 4; - static final int ABORT_UNABLE_TO_DETERMINE = 5; - private static final int OSD_NAME_MAX_LENGTH = 13; private HdmiCecMessageBuilder() {} @@ -290,6 +281,64 @@ public class HdmiCecMessageBuilder { } /** + * Build <System Audio Mode Request> command. + * + * @param src source address of command + * @param avr destination address of command, it should be AVR + * @param avrPhysicalAddress physical address of AVR + * @param enableSystemAudio whether to enable System Audio Mode or not + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildSystemAudioModeRequest(int src, int avr, int avrPhysicalAddress, + boolean enableSystemAudio) { + if (enableSystemAudio) { + return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST, + physicalAddressToParam(avrPhysicalAddress)); + } else { + return buildCommand(src, avr, HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST); + } + } + + /** + * Build <Give Audio Status> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildGiveAudioStatus(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_AUDIO_STATUS); + } + + /** + * Build <User Control Pressed> command. + * + * @param src source address of command + * @param dest destination address of command + * @param uiCommand keycode that user pressed + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) { + byte[] params = new byte[] { + (byte) uiCommand + }; + return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params); + } + + /** + * Build <User Control Released> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildUserControlReleased(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED); + } + + /***** Please ADD new buildXXX() methods above. ******/ + + /** * Build a {@link HdmiCecMessage} without extra parameter. * * @param src source address of command diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java new file mode 100644 index 000000000000..a83d1edcd78d --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +/** + * Defines constants related to HDMI-CEC protocol internal implementation. + * If a constant will be used in the public api, it should be located in + * {@link android.hardware.hdmi.HdmiCec}. + */ +final class HdmiConstants { + + // Constants related to operands of HDMI CEC commands. + // Refer to CEC Table 29 in HDMI Spec v1.4b. + // [Abort Reason] + static final int ABORT_UNRECOGNIZED_MODE = 0; + static final int ABORT_NOT_IN_CORRECT_MODE = 1; + static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; + static final int ABORT_INVALID_OPERAND = 3; + static final int ABORT_REFUSED = 4; + static final int ABORT_UNABLE_TO_DETERMINE = 5; + + // [Audio Status] + static final int SYSTEM_AUDIO_STATUS_OFF = 0; + static final int SYSTEM_AUDIO_STATUS_ON = 1; + + // Constants related to UI Command Codes. + // Refer to CEC Table 30 in HDMI Spec v1.4b. + static final int UI_COMMAND_MUTE = 0x43; + static final int UI_COMMAND_MUTE_FUNCTION = 0x65; + static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66; + + private HdmiConstants() { /* cannot be instantiated */ } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d775733ce5c6..0f3fc215aef7 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -118,8 +118,11 @@ public final class HdmiControlService extends SystemService { // TODO: it may need to hold lock if it's accessed from others. private boolean mArcStatusEnabled = false; + // Whether SystemAudioMode is "On" or not. + private boolean mSystemAudioMode; + // Handler running on service thread. It's used to run a task in service thread. - private Handler mHandler = new Handler(); + private final Handler mHandler = new Handler(); public HdmiControlService(Context context) { super(context); @@ -158,6 +161,9 @@ public final class HdmiControlService extends SystemService { } publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); + + // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and + // start to monitor the preference value and invoke SystemAudioActionFromTv if needed. } /** @@ -211,35 +217,43 @@ public final class HdmiControlService extends SystemService { * @param action {@link FeatureAction} to remove */ void removeAction(final FeatureAction action) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - mActions.remove(action); - } - }); + assertRunOnServiceThread(); + mActions.remove(action); } // Remove all actions matched with the given Class type. private <T extends FeatureAction> void removeAction(final Class<T> clazz) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - Iterator<FeatureAction> iter = mActions.iterator(); - while (iter.hasNext()) { - FeatureAction action = iter.next(); - if (action.getClass().equals(clazz)) { - action.clear(); - mActions.remove(action); - } - } + removeActionExcept(clazz, null); + } + + // Remove all actions matched with the given Class type besides |exception|. + <T extends FeatureAction> void removeActionExcept(final Class<T> clazz, + final FeatureAction exception) { + assertRunOnServiceThread(); + Iterator<FeatureAction> iter = mActions.iterator(); + while (iter.hasNext()) { + FeatureAction action = iter.next(); + if (action != exception && action.getClass().equals(clazz)) { + action.clear(); + mActions.remove(action); } - }); + } } private void runOnServiceThread(Runnable runnable) { mHandler.post(runnable); } + void runOnServiceThreadAtFrontOfQueue(Runnable runnable) { + mHandler.postAtFrontOfQueue(runnable); + } + + private void assertRunOnServiceThread() { + if (Looper.myLooper() != mHandler.getLooper()) { + throw new IllegalStateException("Should run on service thread."); + } + } + /** * Change ARC status into the given {@code enabled} status. * @@ -306,8 +320,12 @@ public final class HdmiControlService extends SystemService { case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: handleReportPhysicalAddress(message); return true; - // TODO: Add remaining system information query such as - // <Give Device Power Status> and <Request Active Source> handler. + case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE: + handleSetSystemAudioMode(message); + return true; + case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS: + handleSystemAudioModeStatus(message); + return true; default: return dispatchMessageToAction(message); } @@ -413,7 +431,7 @@ public final class HdmiControlService extends SystemService { sendCecCommand( HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(), message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, - HdmiCecMessageBuilder.ABORT_UNRECOGNIZED_MODE)); + HdmiConstants.ABORT_UNRECOGNIZED_MODE)); return; } @@ -438,6 +456,33 @@ public final class HdmiControlService extends SystemService { return false; } + private void handleSetSystemAudioMode(HdmiCecMessage message) { + if (dispatchMessageToAction(message) || !isMessageForSystemAudio(message)) { + return; + } + SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this, + message.getDestination(), message.getSource(), + HdmiUtils.parseCommandParamSystemAudioStatus(message)); + addAndStartAction(action); + } + + private void handleSystemAudioModeStatus(HdmiCecMessage message) { + if (!isMessageForSystemAudio(message)) { + return; + } + setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message)); + } + + private boolean isMessageForSystemAudio(HdmiCecMessage message) { + if (message.getSource() != HdmiCec.ADDR_AUDIO_SYSTEM + || message.getDestination() != HdmiCec.ADDR_TV + || getAvrDeviceInfo() == null) { + Slog.w(TAG, "Skip abnormal CecMessage: " + message); + return false; + } + return true; + } + // Record class that monitors the event of the caller of being killed. Used to clean up // the listener list and record list accordingly. private final class HotplugEventListenerRecord implements IBinder.DeathRecipient { @@ -627,4 +672,32 @@ public final class HdmiControlService extends SystemService { Slog.e(TAG, "Invoking callback failed:" + e); } } + + HdmiCecDeviceInfo getAvrDeviceInfo() { + return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM); + } + + void setSystemAudioMode(boolean newMode) { + assertRunOnServiceThread(); + if (newMode != mSystemAudioMode) { + // TODO: Need to set the preference for SystemAudioMode. + // TODO: Need to handle the notification of changing the mode and + // to identify the notification should be handled in the service or TvSettings. + mSystemAudioMode = newMode; + } + } + + boolean getSystemAudioMode() { + assertRunOnServiceThread(); + return mSystemAudioMode; + } + + void setAudioStatus(boolean mute, int volume) { + // TODO: Hook up with AudioManager. + } + + boolean isInPresetInstallationMode() { + // TODO: Implement this. + return false; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java new file mode 100644 index 000000000000..ef128ed15ac0 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; + +/** + * Various utilities to handle HDMI CEC messages. + */ +final class HdmiUtils { + + private HdmiUtils() { /* cannot be instantiated */ } + + /** + * Verify if the given address is for the given device type. If not it will throw + * {@link IllegalArgumentException}. + * + * @param logicalAddress the logical address to verify + * @param deviceType the device type to check + * @throw IllegalArgumentException + */ + static void verifyAddressType(int logicalAddress, int deviceType) { + int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); + if (actualDeviceType != deviceType) { + throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType + + ", Actual:" + actualDeviceType); + } + } + + /** + * Check if the given CEC message come from the given address. + * + * @param cmd the CEC message to check + * @param expectedAddress the expected source address of the given message + * @param tag the tag of caller module (for log message) + * @return true if the CEC message comes from the given address + */ + static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) { + int src = cmd.getSource(); + if (src != expectedAddress) { + Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]"); + return false; + } + return true; + } + + /** + * Parse the parameter block of CEC message as [System Audio Status]. + * + * @param cmd the CEC message to parse + * @return true if the given parameter has [ON] value + */ + static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) { + // TODO: Handle the exception when the length is wrong. + return cmd.getParams().length > 0 + && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON; + } +} diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java index 05614a4547e2..08ca306f8372 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java @@ -44,28 +44,15 @@ abstract class RequestArcAction extends FeatureAction { */ RequestArcAction(HdmiControlService service, int sourceAddress, int avrAddress) { super(service, sourceAddress); - verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); - verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; } - private static void verifyAddressType(int logicalAddress, int deviceType) { - int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); - if (actualDeviceType != deviceType) { - throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceType); - } - } - @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) { - return false; - } - - int src = cmd.getSource(); - if (src != mAvrAddress) { - Slog.w(TAG, "Invalid source [Expected:" + mAvrAddress + ", Actual:" + src + "]"); + if (mState != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE + || !HdmiUtils.checkCommandSource(cmd, mAvrAddress, TAG)) { return false; } int opcode = cmd.getOpcode(); diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index e3525d821e05..d53d88db2292 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -46,21 +46,12 @@ final class SetArcTransmissionStateAction extends FeatureAction { SetArcTransmissionStateAction(HdmiControlService service, int sourceAddress, int avrAddress, boolean enabled) { super(service, sourceAddress); - verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); - verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + HdmiUtils.verifyAddressType(sourceAddress, HdmiCec.DEVICE_TV); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); mAvrAddress = avrAddress; mEnabled = enabled; } - // TODO: extract it as separate utility class. - private static void verifyAddressType(int logicalAddress, int deviceType) { - int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); - if (actualDeviceType != deviceType) { - throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceType); - } - } - @Override boolean start() { if (mEnabled) { diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java new file mode 100644 index 000000000000..dde33423be5a --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; + +/** + * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr. + */ +abstract class SystemAudioAction extends FeatureAction { + private static final String TAG = "SystemAudioAction"; + + // State in which waits for <SetSystemAudioMode>. + private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1; + + // State in which waits for <ReportAudioStatus>. + private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2; + + private static final int MAX_SEND_RETRY_COUNT = 2; + + private static final int ON_TIMEOUT_MS = 5000; + private static final int OFF_TIMEOUT_MS = TIMEOUT_MS; + + // Logical address of AV Receiver. + protected final int mAvrLogicalAddress; + + // The target audio status of the action, whether to enable the system audio mode or not. + protected boolean mTargetAudioStatus; + + private int mSendRetryCount = 0; + + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address of source device (TV or STB). + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid + */ + SystemAudioAction(HdmiControlService service, int sourceAddress, int avrAddress, + boolean targetStatus) { + super(service, sourceAddress); + HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM); + mAvrLogicalAddress = avrAddress; + mTargetAudioStatus = targetStatus; + } + + protected void sendSystemAudioModeRequest() { + int avrPhysicalAddress = mService.getAvrDeviceInfo().getPhysicalAddress(); + HdmiCecMessage command = HdmiCecMessageBuilder.buildSystemAudioModeRequest(mSourceAddress, + mAvrLogicalAddress, avrPhysicalAddress, mTargetAudioStatus); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE; + addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS); + } else { + setSystemAudioMode(false); + finish(); + } + } + }); + } + + private void handleSendSystemAudioModeRequestTimeout() { + if (!mTargetAudioStatus // Don't retry for Off case. + || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) { + setSystemAudioMode(false); + finish(); + return; + } + sendSystemAudioModeRequest(); + } + + protected void setSystemAudioMode(boolean mode) { + mService.setSystemAudioMode(mode); + } + + protected void sendGiveAudioStatus() { + HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(mSourceAddress, + mAvrLogicalAddress); + sendCommand(command, new HdmiControlService.SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + if (error == HdmiControlService.SEND_RESULT_SUCCESS) { + mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS; + addTimer(mState, TIMEOUT_MS); + } else { + handleSendGiveAudioStatusFailure(); + } + } + }); + } + + private void handleSendGiveAudioStatusFailure() { + // TODO: Notify the failure status. + + int uiCommand = mService.getSystemAudioMode() + ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION // SystemAudioMode: ON + : HdmiConstants.UI_COMMAND_MUTE_FUNCTION; // SystemAudioMode: OFF + sendUserControlPressedAndReleased(uiCommand); + finish(); + } + + private void sendUserControlPressedAndReleased(int uiCommand) { + sendCommand(HdmiCecMessageBuilder.buildUserControlPressed( + mSourceAddress, mAvrLogicalAddress, uiCommand)); + sendCommand(HdmiCecMessageBuilder.buildUserControlReleased( + mSourceAddress, mAvrLogicalAddress)); + } + + @Override + final boolean processCommand(HdmiCecMessage cmd) { + switch (mState) { + case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: + // TODO: Handle <FeatureAbort> of <SystemAudioModeRequest> + if (cmd.getOpcode() != HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE + || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) { + return false; + } + boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd); + if (receivedStatus == mTargetAudioStatus) { + setSystemAudioMode(receivedStatus); + sendGiveAudioStatus(); + } else { + // Unexpected response, consider the request is newly initiated by AVR. + // To return 'false' will initiate new SystemAudioActionFromAvr by the control + // service. + finish(); + return false; + } + return true; + + case STATE_WAIT_FOR_REPORT_AUDIO_STATUS: + // TODO: Handle <FeatureAbort> of <GiveAudioStatus> + if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS + || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) { + return false; + } + byte[] params = cmd.getParams(); + if (params.length > 0) { + boolean mute = (params[0] & 0x80) == 0x80; + int volume = params[0] & 0x7F; + mService.setAudioStatus(mute, volume); + if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) { + // Toggle AVR's mute status to match with the system audio status. + sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE); + } + } + finish(); + return true; + } + return false; + } + + protected void removeSystemAudioActionInProgress() { + mService.removeActionExcept(SystemAudioActionFromTv.class, this); + mService.removeActionExcept(SystemAudioActionFromAvr.class, this); + } + + @Override + final void handleTimerEvent(int state) { + if (mState != state) { + return; + } + switch (mState) { + case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: + handleSendSystemAudioModeRequestTimeout(); + return; + case STATE_WAIT_FOR_REPORT_AUDIO_STATUS: + handleSendGiveAudioStatusFailure(); + return; + } + } +} diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java new file mode 100644 index 000000000000..c5eb44b44156 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; + +/** + * Feature action that handles System Audio initiated by AVR devices. + */ +final class SystemAudioActionFromAvr extends SystemAudioAction { + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param tvAddress logical address of TV device + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid + */ + SystemAudioActionFromAvr(HdmiControlService service, int tvAddress, int avrAddress, + boolean targetStatus) { + super(service, tvAddress, avrAddress, targetStatus); + HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV); + } + + @Override + boolean start() { + removeSystemAudioActionInProgress(); + handleSystemAudioActionFromAvr(); + return true; + } + + private void handleSystemAudioActionFromAvr() { + if (mTargetAudioStatus == mService.getSystemAudioMode()) { + finish(); + return; + } + if (mService.isInPresetInstallationMode()) { + sendCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand( + mSourceAddress, mAvrLogicalAddress, + HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE, HdmiConstants.ABORT_REFUSED)); + mTargetAudioStatus = false; + sendSystemAudioModeRequest(); + return; + } + // TODO: Stop the action for System Audio Mode initialization if it is running. + if (mTargetAudioStatus) { + setSystemAudioMode(true); + sendGiveAudioStatus(); + } else { + setSystemAudioMode(false); + finish(); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java new file mode 100644 index 000000000000..9994de6f01ff --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import android.hardware.hdmi.HdmiCec; + + +/** + * Feature action that handles System Audio initiated by TV devices. + */ +final class SystemAudioActionFromTv extends SystemAudioAction { + /** + * Constructor + * + * @param service {@link HdmiControlService} instance + * @param tvAddress logical address of TV device + * @param avrAddress logical address of AVR device + * @param targetStatus Whether to enable the system audio mode or not + * @throw IllegalArugmentException if device type of tvAddress is invalid + */ + SystemAudioActionFromTv(HdmiControlService service, int tvAddress, int avrAddress, + boolean targetStatus) { + super(service, tvAddress, avrAddress, targetStatus); + HdmiUtils.verifyAddressType(tvAddress, HdmiCec.DEVICE_TV); + } + + @Override + boolean start() { + // TODO: Check HDMI-CEC is enabled. + // TODO: Move to the waiting state if currently a routing change is in progress. + + removeSystemAudioActionInProgress(); + sendSystemAudioModeRequest(); + return true; + } +} diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index c909a54630f9..737ffda77c93 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -25,6 +25,7 @@ import android.media.session.ISessionControllerCallback; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.media.session.MediaController; +import android.media.session.RemoteVolumeProvider; import android.media.session.RouteCommand; import android.media.session.RouteInfo; import android.media.session.RouteOptions; @@ -33,6 +34,7 @@ import android.media.session.MediaSession; import android.media.session.MediaSessionInfo; import android.media.session.RouteInterface; import android.media.session.PlaybackState; +import android.media.AudioManager; import android.media.MediaMetadata; import android.media.Rating; import android.os.Bundle; @@ -112,6 +114,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private long mLastActiveTime; // End TransportPerformer fields + // Volume handling fields + private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL; + private int mAudioStream = AudioManager.STREAM_MUSIC; + private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE; + private int mMaxVolume = 0; + private int mCurrentVolume = 0; + // End volume handling fields + private boolean mIsActive = false; private boolean mDestroyed = false; @@ -248,6 +258,27 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** + * Send a volume adjustment to the session owner. + * + * @param delta The amount to adjust the volume by. + */ + public void adjustVolumeBy(int delta) { + if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) { + // Nothing to do, the volume cannot be changed + return; + } + mSessionCb.adjustVolumeBy(delta); + } + + public void setVolumeTo(int value) { + if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) { + // Nothing to do. The volume can't be set directly. + return; + } + mSessionCb.setVolumeTo(value); + } + + /** * Set the connection to use for the selected route and notify the app it is * now connected. * @@ -294,14 +325,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * Check if the session is currently performing playback. This will also * return true if the session was recently paused. * + * @param includeRecentlyActive True if playback that was recently paused + * should count, false if it shouldn't. * @return True if the session is performing playback, false otherwise. */ - public boolean isPlaybackActive() { + public boolean isPlaybackActive(boolean includeRecentlyActive) { int state = mPlaybackState == null ? 0 : mPlaybackState.getState(); if (isActiveState(state)) { return true; } - if (state == mPlaybackState.STATE_PAUSED) { + if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) { long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime; if (inactiveTime < ACTIVE_BUFFER) { return true; @@ -311,6 +344,54 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** + * Get the type of playback, either local or remote. + * + * @return The current type of playback. + */ + public int getPlaybackType() { + return mPlaybackType; + } + + /** + * Get the local audio stream being used. Only valid if playback type is + * local. + * + * @return The audio stream the session is using. + */ + public int getAudioStream() { + return mAudioStream; + } + + /** + * Get the type of volume control. Only valid if playback type is remote. + * + * @return The volume control type being used. + */ + public int getVolumeControl() { + return mVolumeControlType; + } + + /** + * Get the max volume that can be set. Only valid if playback type is + * remote. + * + * @return The max volume that can be set. + */ + public int getMaxVolume() { + return mMaxVolume; + } + + /** + * Get the current volume for this session. Only valid if playback type is + * remote. + * + * @return The current volume of the remote playback. + */ + public int getCurrentVolume() { + return mCurrentVolume; + } + + /** * @return True if this session is currently connected to a route. */ public boolean isConnected() { @@ -640,6 +721,40 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mRequests.add(request); } } + + @Override + public void setCurrentVolume(int volume) { + mCurrentVolume = volume; + } + + @Override + public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException { + switch(type) { + case MediaSession.VOLUME_TYPE_LOCAL: + mPlaybackType = type; + int audioStream = arg1; + if (isValidStream(audioStream)) { + mAudioStream = audioStream; + } else { + Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream"); + mAudioStream = AudioManager.STREAM_MUSIC; + } + break; + case MediaSession.VOLUME_TYPE_REMOTE: + mPlaybackType = type; + mVolumeControlType = arg1; + mMaxVolume = arg2; + break; + default: + throw new IllegalArgumentException("Volume handling type " + type + + " not recognized."); + } + } + + private boolean isValidStream(int stream) { + return stream >= AudioManager.STREAM_VOICE_CALL + && stream <= AudioManager.STREAM_NOTIFICATION; + } } class SessionCb { @@ -780,6 +895,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { Slog.e(TAG, "Remote failure in rate.", e); } } + + public void adjustVolumeBy(int delta) { + try { + mCb.onAdjustVolumeBy(delta); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in adjustVolumeBy.", e); + } + } + + public void setVolumeTo(int value) { + try { + mCb.onSetVolumeTo(value); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in adjustVolumeBy.", e); + } + } } class ControllerStub extends ISessionController.Stub { diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 9d8516757626..87665e103fcc 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -26,6 +26,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.media.AudioManager; +import android.media.IAudioService; import android.media.routeprovider.RouteRequest; import android.media.session.ISession; import android.media.session.ISessionCallback; @@ -40,6 +42,7 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.speech.RecognizerIntent; @@ -79,6 +82,7 @@ public class MediaSessionService extends SystemService implements Monitor { private final PowerManager.WakeLock mMediaEventWakeLock; private KeyguardManager mKeyguardManager; + private IAudioService mAudioService; private MediaSessionRecord mPrioritySession; private int mCurrentUserId = -1; @@ -105,6 +109,12 @@ public class MediaSessionService extends SystemService implements Monitor { updateUser(); mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); + mAudioService = getAudioService(); + } + + private IAudioService getAudioService() { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + return IAudioService.Stub.asInterface(b); } /** @@ -703,6 +713,23 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override + public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) + throws RemoteException { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + MediaSessionRecord session = mPriorityStack + .getDefaultVolumeSession(mCurrentUserId); + dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -737,6 +764,49 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags, + MediaSessionRecord session) { + int direction = 0; + int steps = delta; + if (delta > 0) { + direction = 1; + } else if (delta < 0) { + direction = -1; + steps = -delta; + } + if (DEBUG) { + String sessionInfo = session == null ? null : session.getSessionInfo().toString(); + Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags + + ", suggestedStream=" + suggestedStream); + + } + if (session == null) { + for (int i = 0; i < steps; i++) { + try { + mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, + flags, getContext().getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error adjusting default volume.", e); + } + } + } else { + if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) { + for (int i = 0; i < steps; i++) { + try { + mAudioService.adjustSuggestedStreamVolume(direction, + session.getAudioStream(), flags, + getContext().getOpPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error adjusting volume for stream " + + session.getAudioStream(), e); + } + } + } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) { + session.adjustVolumeBy(delta); + } + } + } + private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock, MediaSessionRecord session) { if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) { diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 56236f883e53..803dee296fb2 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -52,6 +52,7 @@ public class MediaSessionStack { private MediaSessionRecord mCachedButtonReceiver; private MediaSessionRecord mCachedDefault; + private MediaSessionRecord mCachedVolumeDefault; private ArrayList<MediaSessionRecord> mCachedActiveList; private ArrayList<MediaSessionRecord> mCachedTransportControlList; @@ -93,6 +94,9 @@ public class MediaSessionStack { mSessions.remove(record); mSessions.add(0, record); clearCache(); + } else if (newState == PlaybackState.STATE_PAUSED) { + // Just clear the volume cache in this case + mCachedVolumeDefault = null; } } @@ -177,6 +181,25 @@ public class MediaSessionStack { return mCachedButtonReceiver; } + public MediaSessionRecord getDefaultVolumeSession(int userId) { + if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { + return mGlobalPrioritySession; + } + if (mCachedVolumeDefault != null) { + return mCachedVolumeDefault; + } + ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); + int size = records.size(); + for (int i = 0; i < size; i++) { + MediaSessionRecord record = records.get(i); + if (record.isPlaybackActive(false)) { + mCachedVolumeDefault = record; + return record; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, UserHandle.USER_ALL); @@ -237,7 +260,7 @@ public class MediaSessionStack { lastLocalIndex++; lastActiveIndex++; lastPublishedIndex++; - } else if (session.isPlaybackActive()) { + } else if (session.isPlaybackActive(true)) { // TODO replace getRoute() == null with real local route check if(session.getRoute() == null) { // Active local sessions get top priority @@ -284,6 +307,7 @@ public class MediaSessionStack { private void clearCache() { mCachedDefault = null; + mCachedVolumeDefault = null; mCachedButtonReceiver = null; mCachedActiveList = null; mCachedTransportControlList = null; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 13cc98c67041..bb9366318578 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -40,6 +40,7 @@ import com.android.internal.R; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.content.NativeLibraryHelper.ApkHandle; import com.android.internal.content.PackageHelper; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -4148,7 +4149,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } PackageParser.Package pkg = scanPackageLI(file, - flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null); + flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null, null); // Don't mess around with apps in system partition. if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { @@ -4215,7 +4216,7 @@ public class PackageManagerService extends IPackageManager.Stub { * Returns null in case of errors and the error code is stored in mLastScanError */ private PackageParser.Package scanPackageLI(File scanFile, - int parseFlags, int scanMode, long currentTime, UserHandle user) { + int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { mLastScanError = PackageManager.INSTALL_SUCCEEDED; String scanPath = scanFile.getPath(); if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath); @@ -4283,7 +4284,7 @@ public class PackageManagerService extends IPackageManager.Stub { mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; return null; } else { - // The current app on the system partion is better than + // The current app on the system partition is better than // what we have updated to on the data partition; switch // back to the system partition version. // At this point, its safely assumed that package installation for @@ -4402,7 +4403,7 @@ public class PackageManagerService extends IPackageManager.Stub { setApplicationInfoPaths(pkg, codePath, resPath); // Note that we invoke the following method only if we are about to unpack an application PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode - | SCAN_UPDATE_SIGNATURE, currentTime, user); + | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride); /* * If the system app should be overridden by a previously installed @@ -4945,7 +4946,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private PackageParser.Package scanPackageLI(PackageParser.Package pkg, - int parseFlags, int scanMode, long currentTime, UserHandle user) { + int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) { File scanFile = new File(pkg.mScanPath); if (scanFile == null || pkg.applicationInfo.sourceDir == null || pkg.applicationInfo.publicSourceDir == null) { @@ -5395,7 +5396,22 @@ public class PackageManagerService extends IPackageManager.Stub { * only for non-system apps and system app upgrades. */ if (pkg.applicationInfo.nativeLibraryDir != null) { + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); try { + // Enable gross and lame hacks for apps that are built with old + // SDK tools. We must scan their APKs for renderscript bitcode and + // not launch them if it's present. Don't bother checking on devices + // that don't have 64 bit support. + String[] abiList = Build.SUPPORTED_ABIS; + boolean hasLegacyRenderscriptBitcode = false; + if (abiOverride != null) { + abiList = new String[] { abiOverride }; + } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + hasLegacyRenderscriptBitcode = true; + } + File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); final String dataPathString = dataPath.getCanonicalPath(); @@ -5411,21 +5427,26 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "removed obsolete native libraries for system package " + path); } - - setInternalAppAbi(pkg, pkgSetting); + if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; + pkgSetting.cpuAbiString = abiList[0]; + } else { + setInternalAppAbi(pkg, pkgSetting); + } } else { if (!isForwardLocked(pkg) && !isExternal(pkg)) { /* - * Update native library dir if it starts with - * /data/data - */ + * Update native library dir if it starts with + * /data/data + */ if (nativeLibraryDir.getPath().startsWith(dataPathString)) { setInternalAppNativeLibraryPath(pkg, pkgSetting); nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir); } try { - int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir); + int copyRet = copyNativeLibrariesForInternalApp(handle, + nativeLibraryDir, abiList); if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { Slog.e(TAG, "Unable to copy native libraries"); mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; @@ -5435,7 +5456,9 @@ public class PackageManagerService extends IPackageManager.Stub { // We've successfully copied native libraries across, so we make a // note of what ABI we're using if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet]; + pkg.applicationInfo.cpuAbi = abiList[copyRet]; + } else if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; } else { pkg.applicationInfo.cpuAbi = null; } @@ -5452,20 +5475,22 @@ public class PackageManagerService extends IPackageManager.Stub { // to clean this up but we'll need to change the interface between this service // and IMediaContainerService (but doing so will spread this logic out, rather // than centralizing it). - final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); - final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); + final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); if (abi >= 0) { - pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi]; + pkg.applicationInfo.cpuAbi = abiList[abi]; } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { // Note that (non upgraded) system apps will not have any native // libraries bundled in their APK, but we're guaranteed not to be // such an app at this point. - pkg.applicationInfo.cpuAbi = null; + if (abiOverride != null || hasLegacyRenderscriptBitcode) { + pkg.applicationInfo.cpuAbi = abiList[0]; + } else { + pkg.applicationInfo.cpuAbi = null; + } } else { mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; } - handle.close(); } if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path); @@ -5482,8 +5507,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + pkgSetting.cpuAbiString = pkg.applicationInfo.cpuAbi; } catch (IOException ioe) { Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); + } finally { + handle.close(); } } pkg.mScanPath = path; @@ -6175,8 +6204,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private static int copyNativeLibrariesForInternalApp(File scanFile, final File nativeLibraryDir) - throws IOException { + private static int copyNativeLibrariesForInternalApp(ApkHandle handle, + final File nativeLibraryDir, String[] abiList) throws IOException { if (!nativeLibraryDir.isDirectory()) { nativeLibraryDir.delete(); @@ -6198,21 +6227,16 @@ public class PackageManagerService extends IPackageManager.Stub { * If this is an internal application or our nativeLibraryPath points to * the app-lib directory, unpack the libraries if necessary. */ - final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); - try { - int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); - if (abi >= 0) { - int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, - nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); - if (copyRet != PackageManager.INSTALL_SUCCEEDED) { - return copyRet; - } + int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList); + if (abi >= 0) { + int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, + nativeLibraryDir, Build.SUPPORTED_ABIS[abi]); + if (copyRet != PackageManager.INSTALL_SUCCEEDED) { + return copyRet; } - - return abi; - } finally { - handle.close(); } + + return abi; } private void killApplication(String pkgName, int appId, String reason) { @@ -7536,7 +7560,7 @@ public class PackageManagerService extends IPackageManager.Stub { } p = scanPackageLI(fullPath, flags, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, - System.currentTimeMillis(), UserHandle.ALL); + System.currentTimeMillis(), UserHandle.ALL, null); if (p != null) { /* * TODO this seems dangerous as the package may have @@ -7657,6 +7681,16 @@ public class PackageManagerService extends IPackageManager.Stub { if (observer == null && observer2 == null) { throw new IllegalArgumentException("No install observer supplied"); } + installPackageWithVerificationEncryptionAndAbiOverrideEtc(packageURI, observer, observer2, + flags, installerPackageName, verificationParams, encryptionParams, null); + } + + @Override + public void installPackageWithVerificationEncryptionAndAbiOverrideEtc(Uri packageURI, + IPackageInstallObserver observer, IPackageInstallObserver2 observer2, + int flags, String installerPackageName, + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams, + String packageAbiOverride) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); @@ -7696,7 +7730,8 @@ public class PackageManagerService extends IPackageManager.Stub { final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, observer2, filteredFlags, - installerPackageName, verificationParams, encryptionParams, user); + installerPackageName, verificationParams, encryptionParams, user, + packageAbiOverride); mHandler.sendMessage(msg); } @@ -8405,11 +8440,14 @@ public class PackageManagerService extends IPackageManager.Stub { private int mRet; private File mTempPackage; final ContainerEncryptionParams encryptionParams; + final String packageAbiOverride; + final String packageInstructionSetOverride; InstallParams(Uri packageURI, IPackageInstallObserver observer, IPackageInstallObserver2 observer2, int flags, String installerPackageName, VerificationParams verificationParams, - ContainerEncryptionParams encryptionParams, UserHandle user) { + ContainerEncryptionParams encryptionParams, UserHandle user, + String packageAbiOverride) { super(user); this.mPackageURI = packageURI; this.flags = flags; @@ -8418,6 +8456,9 @@ public class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; this.verificationParams = verificationParams; this.encryptionParams = encryptionParams; + this.packageAbiOverride = packageAbiOverride; + this.packageInstructionSetOverride = (packageAbiOverride == null) ? + packageAbiOverride : VMRuntime.getInstructionSet(packageAbiOverride); } @Override @@ -8563,7 +8604,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Remote call to find out default install location final String packageFilePath = packageFile.getAbsolutePath(); pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, - lowThreshold); + lowThreshold, packageAbiOverride); /* * If we have too little free space, try to free cache @@ -8572,10 +8613,10 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { final long size = mContainerService.calculateInstalledSize( - packageFilePath, isForwardLocked()); + packageFilePath, isForwardLocked(), packageAbiOverride); if (mInstaller.freeCache(size + lowThreshold) >= 0) { pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, - flags, lowThreshold); + flags, lowThreshold, packageAbiOverride); } /* * The cache free must have deleted the file we @@ -8995,11 +9036,12 @@ public class PackageManagerService extends IPackageManager.Stub { final ManifestDigest manifestDigest; final UserHandle user; final String instructionSet; + final String abiOverride; InstallArgs(Uri packageURI, IPackageInstallObserver observer, IPackageInstallObserver2 observer2, int flags, String installerPackageName, ManifestDigest manifestDigest, - UserHandle user, String instructionSet) { + UserHandle user, String instructionSet, String abiOverride) { this.packageURI = packageURI; this.flags = flags; this.observer = observer; @@ -9008,6 +9050,7 @@ public class PackageManagerService extends IPackageManager.Stub { this.manifestDigest = manifestDigest; this.user = user; this.instructionSet = instructionSet; + this.abiOverride = abiOverride; } abstract void createCopyFile(); @@ -9063,12 +9106,13 @@ public class PackageManagerService extends IPackageManager.Stub { FileInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser(), null /* instruction set */); + params.getUser(), params.packageInstructionSetOverride, + params.packageAbiOverride); } FileInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet) { - super(null, null, null, 0, null, null, null, instructionSet); + super(null, null, null, 0, null, null, null, instructionSet, null); File codeFile = new File(fullCodePath); installDir = codeFile.getParentFile(); codeFileName = fullCodePath; @@ -9077,7 +9121,7 @@ public class PackageManagerService extends IPackageManager.Stub { } FileInstallArgs(Uri packageURI, String pkgName, String dataDir, String instructionSet) { - super(packageURI, null, null, 0, null, null, null, instructionSet); + super(packageURI, null, null, 0, null, null, null, instructionSet, null); installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir; String apkName = getNextCodePath(null, pkgName, ".apk"); codeFileName = new File(installDir, apkName + ".apk").getPath(); @@ -9181,14 +9225,26 @@ public class PackageManagerService extends IPackageManager.Stub { NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile); nativeLibraryFile.delete(); } + + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile); + String[] abiList = (abiOverride != null) ? + new String[] { abiOverride } : Build.SUPPORTED_ABIS; try { - int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile); + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && + abiOverride == null && + NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + } + + int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList); if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { return copyRet; } } catch (IOException e) { Slog.e(TAG, "Copying native libraries failed", e); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } finally { + handle.close(); } return ret; @@ -9403,14 +9459,15 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(InstallParams params) { super(params.getPackageUri(), params.observer, params.observer2, params.flags, params.installerPackageName, params.getManifestDigest(), - params.getUser(), null /* instruction set */); + params.getUser(), params.packageInstructionSetOverride, + params.packageAbiOverride); } AsecInstallArgs(String fullCodePath, String fullResourcePath, String nativeLibraryPath, String instructionSet, boolean isExternal, boolean isForwardLocked) { super(null, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); // Extract cid from fullCodePath int eidx = fullCodePath.lastIndexOf("/"); String subStr1 = fullCodePath.substring(0, eidx); @@ -9422,7 +9479,7 @@ public class PackageManagerService extends IPackageManager.Stub { AsecInstallArgs(String cid, String instructionSet, boolean isForwardLocked) { super(null, null, null, (isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); this.cid = cid; setCachePath(PackageHelper.getSdDir(cid)); } @@ -9431,7 +9488,7 @@ public class PackageManagerService extends IPackageManager.Stub { boolean isExternal, boolean isForwardLocked) { super(packageURI, null, null, (isExternal ? PackageManager.INSTALL_EXTERNAL : 0) | (isForwardLocked ? PackageManager.INSTALL_FORWARD_LOCK : 0), - null, null, null, instructionSet); + null, null, null, instructionSet, null); this.cid = cid; } @@ -9443,7 +9500,7 @@ public class PackageManagerService extends IPackageManager.Stub { try { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); - return imcs.checkExternalFreeStorage(packageURI, isFwdLocked()); + return imcs.checkExternalFreeStorage(packageURI, isFwdLocked(), abiOverride); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -9469,7 +9526,8 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(), - RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked()); + RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked(), + abiOverride); } finally { mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION); } @@ -9777,7 +9835,7 @@ public class PackageManagerService extends IPackageManager.Stub { */ private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; @@ -9805,7 +9863,7 @@ public class PackageManagerService extends IPackageManager.Stub { } mLastScanError = PackageManager.INSTALL_SUCCEEDED; PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, - System.currentTimeMillis(), user); + System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -9832,7 +9890,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void replacePackageLI(PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { PackageParser.Package oldPackage; String pkgName = pkg.packageName; @@ -9861,17 +9919,19 @@ public class PackageManagerService extends IPackageManager.Stub { boolean sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { replaceSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, - user, allUsers, perUserInstalled, installerPackageName, res); + user, allUsers, perUserInstalled, installerPackageName, res, + abiOverride); } else { replaceNonSystemPackageLI(oldPackage, pkg, parseFlags, scanMode, - user, allUsers, perUserInstalled, installerPackageName, res); + user, allUsers, perUserInstalled, installerPackageName, res, + abiOverride); } } private void replaceNonSystemPackageLI(PackageParser.Package deletedPackage, PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, int[] allUsers, boolean[] perUserInstalled, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { PackageParser.Package newPackage = null; String pkgName = deletedPackage.packageName; boolean deletedPkg = true; @@ -9896,7 +9956,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully deleted the old package. Now proceed with re-installation mLastScanError = PackageManager.INSTALL_SUCCEEDED; newPackage = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_TIME, - System.currentTimeMillis(), user); + System.currentTimeMillis(), user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -9925,7 +9985,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Since we failed to install the new package we need to restore the old // package that we deleted. - if(deletedPkg) { + if (deletedPkg) { if (DEBUG_INSTALL) Slog.d(TAG, "Install failed, reinstalling: " + deletedPackage); File restoreFile = new File(deletedPackage.mPath); // Parse old package @@ -9936,7 +9996,7 @@ public class PackageManagerService extends IPackageManager.Stub { int oldScanMode = (oldOnSd ? 0 : SCAN_MONITOR) | SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME; if (scanPackageLI(restoreFile, oldParseFlags, oldScanMode, - origUpdateTime, null) == null) { + origUpdateTime, null, null) == null) { Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade"); return; } @@ -9956,7 +10016,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void replaceSystemPackageLI(PackageParser.Package deletedPackage, PackageParser.Package pkg, int parseFlags, int scanMode, UserHandle user, int[] allUsers, boolean[] perUserInstalled, - String installerPackageName, PackageInstalledInfo res) { + String installerPackageName, PackageInstalledInfo res, String abiOverride) { if (DEBUG_INSTALL) Slog.d(TAG, "replaceSystemPackageLI: new=" + pkg + ", old=" + deletedPackage); PackageParser.Package newPackage = null; @@ -10010,7 +10070,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Successfully disabled the old package. Now proceed with re-installation res.returnCode = mLastScanError = PackageManager.INSTALL_SUCCEEDED; pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user); + newPackage = scanPackageLI(pkg, parseFlags, scanMode, 0, user, abiOverride); if (newPackage == null) { Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { @@ -10044,7 +10104,7 @@ public class PackageManagerService extends IPackageManager.Stub { removeInstalledPackageLI(newPackage, true); } // Add back the old system package - scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user); + scanPackageLI(oldPkg, parseFlags, SCAN_MONITOR | SCAN_UPDATE_SIGNATURE, 0, user, null); // Restore the old system information in Settings synchronized(mPackages) { if (updatedSettings) { @@ -10278,10 +10338,10 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath(); if (replace) { replacePackageLI(pkg, parseFlags, scanMode, args.user, - installerPackageName, res); + installerPackageName, res, args.abiOverride); } else { installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user, - installerPackageName, res); + installerPackageName, res, args.abiOverride); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); @@ -10698,7 +10758,7 @@ public class PackageManagerService extends IPackageManager.Stub { parseFlags |= PackageParser.PARSE_IS_PRIVILEGED; } PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath, - parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null); + parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null, null); if (newPkg == null) { Slog.w(TAG, "Failed to restore system package:" + newPs.name @@ -12511,7 +12571,7 @@ public class PackageManagerService extends IPackageManager.Stub { doGc = true; synchronized (mInstallLock) { final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags, - 0, 0, null); + 0, 0, null, null); // Scan the package if (pkg != null) { /* diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 81db8b306aa0..a354c4570d40 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -174,14 +174,14 @@ public class TaskStack { stackNdx = 0; } else { stackNdx = mTasks.size(); - final int currentUserId = mService.mCurrentUserId; - if (task.mUserId != currentUserId) { + if (!mService.isCurrentProfileLocked(task.mUserId)) { // Place the task below all current user tasks. while (--stackNdx >= 0) { - if (currentUserId != mTasks.get(stackNdx).mUserId) { + if (!mService.isCurrentProfileLocked(mTasks.get(stackNdx).mUserId)) { break; } } + // Put it above first non-current user task. ++stackNdx; } } @@ -352,7 +352,7 @@ public class TaskStack { int top = mTasks.size(); for (int taskNdx = 0; taskNdx < top; ++taskNdx) { Task task = mTasks.get(taskNdx); - if (task.mUserId == userId) { + if (mService.isCurrentProfileLocked(task.mUserId)) { mTasks.remove(taskNdx); mTasks.add(task); --top; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9174c0ccf440..164fe055a070 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -127,7 +127,7 @@ public final class SystemServer { private static final String WIFI_SERVICE_CLASS = "com.android.server.wifi.WifiService"; private static final String WIFI_PASSPOINT_SERVICE_CLASS = - "com.android.server.wifi.passpoint.PasspointService"; + "com.android.server.wifi.passpoint.WifiPasspointService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; private static final String HDMI_CEC_SERVICE_CLASS = @@ -644,15 +644,15 @@ public final class SystemServer { } try { - mSystemServiceManager.startService(WIFI_SERVICE_CLASS); + mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS); } catch (Throwable e) { - reportWtf("starting Wi-Fi Service", e); + reportWtf("starting Wi-Fi PasspointService", e); } try { - mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS); + mSystemServiceManager.startService(WIFI_SERVICE_CLASS); } catch (Throwable e) { - reportWtf("starting Wi-Fi PasspointService", e); + reportWtf("starting Wi-Fi Service", e); } try { diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java index 51f10c1028ae..d4521727bb45 100644 --- a/telecomm/java/android/telecomm/CallService.java +++ b/telecomm/java/android/telecomm/CallService.java @@ -27,6 +27,8 @@ import com.android.internal.os.SomeArgs; import com.android.internal.telecomm.ICallService; import com.android.internal.telecomm.ICallServiceAdapter; +import java.util.List; + /** * Base implementation of CallService which can be used to provide calls for the system * in-call UI. CallService is a one-way service from the framework's CallsManager to any app @@ -59,6 +61,8 @@ public abstract class CallService extends Service { private static final int MSG_ON_AUDIO_STATE_CHANGED = 11; private static final int MSG_PLAY_DTMF_TONE = 12; private static final int MSG_STOP_DTMF_TONE = 13; + private static final int MSG_ADD_TO_CONFERENCE = 14; + private static final int MSG_SPLIT_FROM_CONFERENCE = 15; /** * Default Handler used to consolidate binder method calls onto a single thread. @@ -123,6 +127,29 @@ public abstract class CallService extends Service { case MSG_STOP_DTMF_TONE: stopDtmfTone((String) msg.obj); break; + case MSG_ADD_TO_CONFERENCE: { + SomeArgs args = (SomeArgs) msg.obj; + try { + @SuppressWarnings("unchecked") + List<String> callIds = (List<String>) args.arg2; + String conferenceCallId = (String) args.arg1; + addToConference(conferenceCallId, callIds); + } finally { + args.recycle(); + } + break; + } + case MSG_SPLIT_FROM_CONFERENCE: { + SomeArgs args = (SomeArgs) msg.obj; + try { + String conferenceCallId = (String) args.arg1; + String callId = (String) args.arg2; + splitFromConference(conferenceCallId, callId); + } finally { + args.recycle(); + } + break; + } default: break; } @@ -204,6 +231,22 @@ public abstract class CallService extends Service { args.arg2 = audioState; mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget(); } + + @Override + public void addToConference(String conferenceCallId, List<String> callsToConference) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = conferenceCallId; + args.arg2 = callsToConference; + mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget(); + } + + @Override + public void splitFromConference(String conferenceCallId, String callId) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = conferenceCallId; + args.arg2 = callId; + mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); + } } /** @@ -359,4 +402,24 @@ public abstract class CallService extends Service { * @param audioState The new {@link CallAudioState}. */ public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); + + /** + * Adds the specified calls to the specified conference call. + * + * @param conferenceCallId The unique ID of the conference call onto which the specified calls + * should be added. + * @param callIds The calls to add to the conference call. + * @hide + */ + public abstract void addToConference(String conferenceCallId, List<String> callIds); + + /** + * Removes the specified call from the specified conference call. This is a no-op if the call + * is not already part of the conference call. + * + * @param conferenceCallId The conference call. + * @param callId The call to remove from the conference call + * @hide + */ + public abstract void splitFromConference(String conferenceCallId, String callId); } diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java index dafc3104edb3..7396808025ff 100644 --- a/telecomm/java/android/telecomm/CallServiceAdapter.java +++ b/telecomm/java/android/telecomm/CallServiceAdapter.java @@ -20,6 +20,8 @@ import android.os.RemoteException; import com.android.internal.telecomm.ICallServiceAdapter; +import java.util.List; + /** * Provides methods for ICallService implementations to interact with the system phone app. * TODO(santoscordon): Need final public-facing comments in this file. @@ -169,4 +171,46 @@ public final class CallServiceAdapter { } } + /** + * Indicates that the specified call can conference with any of the specified list of calls. + * + * @param callId The unique ID of the call. + * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced. + * @hide + */ + public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) { + try { + mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds); + } catch (RemoteException ignored) { + } + } + + /** + * Indicates whether or not the specified call is currently conferenced into the specified + * conference call. + * + * @param conferenceCallId The unique ID of the conference call. + * @param callId The unique ID of the call being conferenced. + * @hide + */ + public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) { + try { + mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced); + } catch (RemoteException ignored) { + } + } + + /** + * Indicates that the call no longer exists. Can be used with either a call or a conference + * call. + * + * @param callId The unique ID of the call. + * @hide + */ + public void removeCall(String callId) { + try { + mAdapter.removeCall(callId); + } catch (RemoteException ignored) { + } + } } diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index 8d02842dc5a6..aeb1c3356783 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -20,6 +20,8 @@ import android.net.Uri; import android.os.Bundle; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; /** @@ -248,6 +250,39 @@ public abstract class ConnectionService extends CallService { findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState); } + /** @hide */ + @Override + public final void addToConference(String conferenceCallId, List<String> callIds) { + Log.d(this, "addToConference %s, %s", conferenceCallId, callIds); + + List<Connection> connections = new LinkedList<>(); + for (String id : callIds) { + Connection connection = findConnectionForAction(id, "addToConference"); + if (connection == NULL_CONNECTION) { + Log.w(this, "Connection missing in conference request %s.", id); + return; + } + connections.add(connection); + } + + // TODO(santoscordon): Find an existing conference call or create a new one. Then call + // conferenceWith on it. + } + + /** @hide */ + @Override + public final void splitFromConference(String conferenceCallId, String callId) { + Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId); + + Connection connection = findConnectionForAction(callId, "splitFromConference"); + if (connection == NULL_CONNECTION) { + Log.w(this, "Connection missing in conference request %s.", callId); + return; + } + + // TODO(santoscordon): Find existing conference call and invoke split(connection). + } + /** * Find a set of Subscriptions matching a given handle (e.g. phone number). * @@ -342,4 +377,4 @@ public abstract class ConnectionService extends CallService { Log.w(this, "%s - Cannot find Connection %s", action, callId); return NULL_CONNECTION; } -}
\ No newline at end of file +} diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index e41d3f638bce..6838ede45470 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -196,4 +196,32 @@ public final class InCallAdapter { } catch (RemoteException e) { } } + + /** + * Instructs Telecomm to conference the specified calls together. + * + * @param callId The unique ID of the call. + * @param callIdToConference The unique ID of the call to conference with. + * @hide + */ + void conferenceWith(String callId, String callIdToConference) { + try { + mAdapter.conferenceWith(callId, callIdToConference); + } catch (RemoteException ignored) { + } + } + + /** + * Instructs Telecomm to split the specified call from any conference call with which it may be + * connected. + * + * @param callId The unique ID of the call. + * @hide + */ + void splitFromConference(String callId) { + try { + mAdapter.splitFromConference(callId); + } catch (RemoteException ignored) { + } + } } diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java index c3b2ae7a2029..346d2077a52a 100644 --- a/telecomm/java/android/telecomm/InCallCall.java +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -17,13 +17,13 @@ package android.telecomm; import android.net.Uri; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.telephony.DisconnectCause; -import java.util.Date; -import java.util.UUID; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Information about a call that is used between InCallService and Telecomm. @@ -38,8 +38,12 @@ public final class InCallCall implements Parcelable { private final GatewayInfo mGatewayInfo; private final CallServiceDescriptor mCurrentCallServiceDescriptor; private final CallServiceDescriptor mHandoffCallServiceDescriptor; + private final List<String> mConferenceCapableCallIds; + private final String mParentCallId; + private final List<String> mChildCallIds; /** @hide */ + @SuppressWarnings("unchecked") public InCallCall( String id, CallState state, @@ -50,6 +54,25 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo, CallServiceDescriptor descriptor, CallServiceDescriptor handoffDescriptor) { + this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo, + descriptor, handoffDescriptor, Collections.EMPTY_LIST, null, + Collections.EMPTY_LIST); + } + + /** @hide */ + public InCallCall( + String id, + CallState state, + int disconnectCause, + int capabilities, + long connectTimeMillis, + Uri handle, + GatewayInfo gatewayInfo, + CallServiceDescriptor descriptor, + CallServiceDescriptor handoffDescriptor, + List<String> conferenceCapableCallIds, + String parentCallId, + List<String> childCallIds) { mId = id; mState = state; mDisconnectCause = disconnectCause; @@ -59,6 +82,9 @@ public final class InCallCall implements Parcelable { mGatewayInfo = gatewayInfo; mCurrentCallServiceDescriptor = descriptor; mHandoffCallServiceDescriptor = handoffDescriptor; + mConferenceCapableCallIds = conferenceCapableCallIds; + mParentCallId = parentCallId; + mChildCallIds = childCallIds; } /** The unique ID of the call. */ @@ -112,6 +138,31 @@ public final class InCallCall implements Parcelable { return mHandoffCallServiceDescriptor; } + /** + * The calls with which this call can conference. + * @hide + */ + public List<String> getConferenceCapableCallIds() { + return mConferenceCapableCallIds; + } + + /** + * The conference call to which this call is conferenced. Null if not conferenced. + * @hide + */ + public String getParentCallId() { + return mParentCallId; + } + + /** + * The child call-IDs if this call is a conference call. Returns an empty list if this is not + * a conference call or if the conference call contains no children. + * @hide + */ + public List<String> getChildCallIds() { + return mChildCallIds; + } + /** Responsible for creating InCallCall objects for deserialized Parcels. */ public static final Parcelable.Creator<InCallCall> CREATOR = new Parcelable.Creator<InCallCall> () { @@ -127,8 +178,14 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo = source.readParcelable(classLoader); CallServiceDescriptor descriptor = source.readParcelable(classLoader); CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader); + List<String> conferenceCapableCallIds = new ArrayList<>(); + source.readList(conferenceCapableCallIds, classLoader); + String parentCallId = source.readString(); + List<String> childCallIds = new ArrayList<>(); + source.readList(childCallIds, classLoader); return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis, - handle, gatewayInfo, descriptor, handoffDescriptor); + handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds, + parentCallId, childCallIds); } @Override @@ -155,5 +212,8 @@ public final class InCallCall implements Parcelable { destination.writeParcelable(mGatewayInfo, 0); destination.writeParcelable(mCurrentCallServiceDescriptor, 0); destination.writeParcelable(mHandoffCallServiceDescriptor, 0); + destination.writeList(mConferenceCapableCallIds); + destination.writeString(mParentCallId); + destination.writeList(mChildCallIds); } } diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl index cc0641ca8865..771a3ae9ef38 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl @@ -55,4 +55,8 @@ oneway interface ICallService { void playDtmfTone(String callId, char digit); void stopDtmfTone(String callId); + + void addToConference(String conferenceCallId, in List<String> callIds); + + void splitFromConference(String conferenceCallId, String callId); } diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl index 6d36494e498d..a92b1767dbd8 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl @@ -45,4 +45,10 @@ oneway interface ICallServiceAdapter { void setOnHold(String callId); void setRequestingRingback(String callId, boolean ringing); + + void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds); + + void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced); + + void removeCall(String callId); } diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl index 512e8981b1d2..6a27217a9275 100644 --- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl @@ -47,4 +47,8 @@ oneway interface IInCallAdapter { void postDialContinue(String callId); void handoffCall(String callId); + + void conferenceWith(String callId, String callIdToConference); + + void splitFromConference(String callId); } diff --git a/test-runner/src/android/test/MoreAsserts.java b/test-runner/src/android/test/MoreAsserts.java index fb0fabacab4b..33648953e57d 100644 --- a/test-runner/src/android/test/MoreAsserts.java +++ b/test-runner/src/android/test/MoreAsserts.java @@ -128,6 +128,33 @@ public final class MoreAsserts { } /** + * @hide Asserts that array {@code actual} is the same size and every element equals + * those in array {@code expected}. On failure, message indicates first + * specific element mismatch. + */ + public static void assertEquals( + String message, long[] expected, long[] actual) { + if (expected.length != actual.length) { + failWrongLength(message, expected.length, actual.length); + } + for (int i = 0; i < expected.length; i++) { + if (expected[i] != actual[i]) { + failWrongElement(message, i, expected[i], actual[i]); + } + } + } + + /** + * @hide Asserts that array {@code actual} is the same size and every element equals + * those in array {@code expected}. On failure, message indicates first + * specific element mismatch. + */ + public static void assertEquals(long[] expected, long[] actual) { + assertEquals(null, expected, actual); + } + + + /** * Asserts that array {@code actual} is the same size and every element equals * those in array {@code expected}. On failure, message indicates first * specific element mismatch. diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java index 5b0aa66f1a54..a81e0637480b 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java @@ -118,19 +118,25 @@ public class CirclePropActivity extends Activity { mRadius, mToggle ? 250.0f : 150.0f)); mRunningAnimations.add(new RenderNodeAnimator( - mPaint, RenderNodeAnimator.PAINT_ALPHA, - mToggle ? 64.0f : 255.0f)); - - mRunningAnimations.add(new RenderNodeAnimator( mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH, mToggle ? 5.0f : 60.0f)); - TimeInterpolator interp = new OvershootInterpolator(3.0f); + mRunningAnimations.add(new RenderNodeAnimator( + mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f)); + + // Will be "chained" to run after the above + mRunningAnimations.add(new RenderNodeAnimator( + mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f)); + for (int i = 0; i < mRunningAnimations.size(); i++) { RenderNodeAnimator anim = mRunningAnimations.get(i); - anim.setInterpolator(interp); anim.setDuration(1000); anim.setTarget(this); + if (i == (mRunningAnimations.size() - 1)) { + // "chain" test + anim.setStartValue(64.0f); + anim.setStartDelay(anim.getDuration()); + } anim.start(); } diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java index c1fa74f5a7a3..d6f811820e19 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java @@ -82,7 +82,7 @@ public class PlayerSession { Log.d(TAG, "Creating session for package " + mContext.getBasePackageName()); mSession = man.createSession("OneMedia"); mSession.addCallback(mCallback); - mSession.addTransportControlsCallback(new TransportListener()); + mSession.addTransportControlsCallback(new TransportCallback()); mSession.setPlaybackState(mPlaybackState); mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setRouteOptions(mRouteOptions); @@ -255,7 +255,7 @@ public class PlayerSession { } } - private class TransportListener extends MediaSession.TransportControlsCallback { + private class TransportCallback extends MediaSession.TransportControlsCallback { @Override public void onPlay() { mRenderer.onPlay(); diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index f6d7f5534ad9..99151c36d333 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -144,8 +144,7 @@ public class ScanResult implements Parcelable { distanceCm = source.distanceCm; distanceSdCm = source.distanceSdCm; seen = source.seen; - if (source.passpoint != null) - passpoint = new WifiPasspointInfo(source.passpoint); + passpoint = source.passpoint; } } @@ -179,8 +178,7 @@ public class ScanResult implements Parcelable { sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")). append("(cm)"); - if (passpoint != null) - sb.append(", passpoint: [").append(passpoint.toString()).append("]"); + sb.append(", passpoint: ").append(passpoint != null ? "yes" : "no"); return sb.toString(); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 0faaebae182f..5dfc3184193c 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -495,6 +495,12 @@ public class WifiConfiguration implements Parcelable { public boolean didSelfAdd; /** + * peer WifiConfiguration this WifiConfiguration was added for + * @hide + */ + public String peerWifiConfiguration; + + /** * @hide * Indicate that a WifiConfiguration is temporary and should not be saved * nor considered by AutoJoin. @@ -980,6 +986,7 @@ public class WifiConfiguration implements Parcelable { lastConnectUid = source.lastConnectUid; lastUpdateUid = source.lastUpdateUid; creatorUid = source.creatorUid; + peerWifiConfiguration = source.peerWifiConfiguration; } } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java index 99bea2f8ca45..5ef1bf9ddf57 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java @@ -88,68 +88,174 @@ public class WifiPasspointInfo implements Parcelable { CONNECTION_CAPABILITY | OSU_PROVIDER; - /** TODO doc */ - public String bssid; - /** TODO doc */ - public String venueName; + public static class WanMetrics { + public static final int STATUS_RESERVED = 0; + public static final int STATUS_UP = 1; + public static final int STATUS_DOWN = 2; + public static final int STATUS_TEST = 3; + + public int wanInfo; + public long downlinkSpeed; + public long uplinkSpeed; + public int downlinkLoad; + public int uplinkLoad; + public int lmd; + + public int getLinkStatus() { + return wanInfo & 0x3; + } - /** TODO doc */ - public String networkAuthType; + public boolean getSymmetricLink() { + return (wanInfo & (1 << 2)) != 0; + } - /** TODO doc */ - public String roamingConsortium; + public boolean getAtCapacity() { + return (wanInfo & (1 << 3)) != 0; + } - /** TODO doc */ - public String ipAddrTypeAvaibility; + @Override + public String toString() { + return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," + + downlinkLoad + "," + uplinkLoad + "," + lmd; + } + } - /** TODO doc */ - public String naiRealm; + public static class IpProtoPort { + public static final int STATUS_CLOSED = 0; + public static final int STATUS_OPEN = 1; + public static final int STATUS_UNKNOWN = 2; - /** TODO doc */ - public String cellularNetwork; + public int proto; + public int port; + public int status; - /** TODO doc */ - public String domainName; + @Override + public String toString() { + return proto + "," + port + "," + status; + } + } - /** TODO doc */ - public String operatorFriendlyName; + public static class NetworkAuthType { + public static final int TYPE_TERMS_AND_CONDITION = 0; + public static final int TYPE_ONLINE_ENROLLMENT = 1; + public static final int TYPE_HTTP_REDIRECTION = 2; + public static final int TYPE_DNS_REDIRECTION = 3; - /** TODO doc */ - public String wanMetrics; + public int type; + public String redirectUrl; - /** TODO doc */ - public String connectionCapability; + @Override + public String toString() { + return type + "," + redirectUrl; + } + } - /** TODO doc */ - public List<WifiPasspointOsuProvider> osuProviderList; + public static class IpAddressType { + public static final int IPV6_NOT_AVAILABLE = 0; + public static final int IPV6_AVAILABLE = 1; + public static final int IPV6_UNKNOWN = 2; + + public static final int IPV4_NOT_AVAILABLE = 0; + public static final int IPV4_PUBLIC = 1; + public static final int IPV4_PORT_RESTRICTED = 2; + public static final int IPV4_SINGLE_NAT = 3; + public static final int IPV4_DOUBLE_NAT = 4; + public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5; + public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6; + public static final int IPV4_PORT_UNKNOWN = 7; + + private static final int NULL_VALUE = -1; + + public int availability; + + public int getIpv6Availability() { + return availability & 0x3; + } + + public int getIpv4Availability() { + return (availability & 0xFF) >> 2; + } + + @Override + public String toString() { + return getIpv6Availability() + "," + getIpv4Availability(); + } + } + + public static class NaiRealm { + public static final int ENCODING_RFC4282 = 0; + public static final int ENCODING_UTF8 = 1; + + public int encoding; + public String realm; - /** default constructor @hide */ - public WifiPasspointInfo() { - // osuProviderList = new ArrayList<OsuProvider>(); + @Override + public String toString() { + return encoding + "," + realm; + } } - /** copy constructor @hide */ - public WifiPasspointInfo(WifiPasspointInfo source) { - // TODO - bssid = source.bssid; - venueName = source.venueName; - networkAuthType = source.networkAuthType; - roamingConsortium = source.roamingConsortium; - ipAddrTypeAvaibility = source.ipAddrTypeAvaibility; - naiRealm = source.naiRealm; - cellularNetwork = source.cellularNetwork; - domainName = source.domainName; - operatorFriendlyName = source.operatorFriendlyName; - wanMetrics = source.wanMetrics; - connectionCapability = source.connectionCapability; - if (source.osuProviderList != null) { - osuProviderList = new ArrayList<WifiPasspointOsuProvider>(); - for (WifiPasspointOsuProvider osu : source.osuProviderList) - osuProviderList.add(new WifiPasspointOsuProvider(osu)); + public static class CellularNetwork { + public byte[] rawData; + + public int getMnc() { + // TODO + return 0; + } + + public int getMcc() { + // TODO + return 0; + } + + @Override + public String toString() { + if (rawData == null) return null; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < rawData.length; i++) + sb.append(String.format("%02X", rawData[i])); + return sb.toString(); } + } + /** BSSID */ + public String bssid; + + /** venue name */ + public String venueName; + + /** list of network authentication types */ + public List<NetworkAuthType> networkAuthType; + + /** list of roaming consortium OIs */ + public List<String> roamingConsortium; + + /** IP address availability */ + public IpAddressType ipAddrTypeAvailability; + + /** NAI realm */ + public List<NaiRealm> naiRealm; + + /** 3GPP cellular network */ + public CellularNetwork cellularNetwork; + + /** fully qualified domain name (FQDN) */ + public List<String> domainName; + + /** HS 2.0 operator friendly name */ + public String operatorFriendlyName; + + /** HS 2.0 wan metrics */ + public WanMetrics wanMetrics; + + /** HS 2.0 list of IP proto port */ + public List<IpProtoPort> connectionCapability; + + /** HS 2.0 list of OSU providers */ + public List<WifiPasspointOsuProvider> osuProviderList; + /** * Convert mask to ANQP subtypes, for supplicant command use. * @@ -193,46 +299,149 @@ public class WifiPasspointInfo implements Parcelable { @Override public String toString() { StringBuffer sb = new StringBuffer(); + sb.append("BSSID: ").append(bssid); + if (venueName != null) - sb.append(" venueName: ").append(venueName); - if (networkAuthType != null) - sb.append(" networkAuthType: ").append(networkAuthType); - if (roamingConsortium != null) - sb.append(" roamingConsortium: ").append(roamingConsortium); - if (ipAddrTypeAvaibility != null) - sb.append(" ipAddrTypeAvaibility: ").append(ipAddrTypeAvaibility); - if (naiRealm != null) - sb.append(" naiRealm: ").append(naiRealm); + sb.append(" venueName: ").append(venueName.replace("\n", "\\n")); + + if (networkAuthType != null) { + sb.append(" networkAuthType: "); + for (NetworkAuthType auth : networkAuthType) + sb.append("(").append(auth.toString()).append(")"); + } + + if (roamingConsortium != null) { + sb.append(" roamingConsortium: "); + for (String oi : roamingConsortium) + sb.append("(").append(oi).append(")"); + } + + if (ipAddrTypeAvailability != null) { + sb.append(" ipAddrTypeAvaibility: ").append("(") + .append(ipAddrTypeAvailability.toString()).append(")"); + } + + if (naiRealm != null) { + sb.append(" naiRealm: "); + for (NaiRealm realm : naiRealm) + sb.append("(").append(realm.toString()).append(")"); + } + if (cellularNetwork != null) - sb.append(" cellularNetwork: ").append(cellularNetwork); - if (domainName != null) - sb.append(" domainName: ").append(domainName); + sb.append(" cellularNetwork: ").append("(") + .append(cellularNetwork.toString()).append(")"); + + if (domainName != null) { + sb.append(" domainName: "); + for (String fqdn : domainName) + sb.append("(").append(fqdn).append(")"); + } + if (operatorFriendlyName != null) - sb.append(" operatorFriendlyName: ").append(operatorFriendlyName); + sb.append(" operatorFriendlyName: ").append("(") + .append(operatorFriendlyName).append(")"); + if (wanMetrics != null) - sb.append(" wanMetrics: ").append(wanMetrics); - if (connectionCapability != null) - sb.append(" connectionCapability: ").append(connectionCapability); - if (osuProviderList != null) - sb.append(" osuProviderList: (size=" + osuProviderList.size() + ")"); + sb.append(" wanMetrics: ").append("(") + .append(wanMetrics.toString()).append(")"); + + if (connectionCapability != null) { + sb.append(" connectionCapability: "); + for (IpProtoPort ip : connectionCapability) + sb.append("(").append(ip.toString()).append(")"); + } + + if (osuProviderList != null) { + sb.append(" osuProviderList: "); + for (WifiPasspointOsuProvider osu : osuProviderList) + sb.append("(").append(osu.toString()).append(")"); + } + return sb.toString(); } /** Implement the Parcelable interface {@hide} */ @Override public void writeToParcel(Parcel out, int flags) { - out.writeValue(bssid); - out.writeValue(venueName); - out.writeValue(networkAuthType); - out.writeValue(roamingConsortium); - out.writeValue(ipAddrTypeAvaibility); - out.writeValue(naiRealm); - out.writeValue(cellularNetwork); - out.writeValue(domainName); - out.writeValue(operatorFriendlyName); - out.writeValue(wanMetrics); - out.writeValue(connectionCapability); + out.writeString(bssid); + out.writeString(venueName); + + if (networkAuthType == null) { + out.writeInt(0); + } else { + out.writeInt(networkAuthType.size()); + for (NetworkAuthType auth : networkAuthType) { + out.writeInt(auth.type); + out.writeString(auth.redirectUrl); + } + } + + if (roamingConsortium == null) { + out.writeInt(0); + } else { + out.writeInt(roamingConsortium.size()); + for (String oi : roamingConsortium) + out.writeString(oi); + } + + if (ipAddrTypeAvailability == null) { + out.writeInt(IpAddressType.NULL_VALUE); + } else { + out.writeInt(ipAddrTypeAvailability.availability); + } + + if (naiRealm == null) { + out.writeInt(0); + } else { + out.writeInt(naiRealm.size()); + for (NaiRealm realm : naiRealm) { + out.writeInt(realm.encoding); + out.writeString(realm.realm); + } + } + + if (cellularNetwork == null) { + out.writeInt(0); + } else { + out.writeInt(cellularNetwork.rawData.length); + out.writeByteArray(cellularNetwork.rawData); + } + + + if (domainName == null) { + out.writeInt(0); + } else { + out.writeInt(domainName.size()); + for (String fqdn : domainName) + out.writeString(fqdn); + } + + out.writeString(operatorFriendlyName); + + if (wanMetrics == null) { + out.writeInt(0); + } else { + out.writeInt(1); + out.writeInt(wanMetrics.wanInfo); + out.writeLong(wanMetrics.downlinkSpeed); + out.writeLong(wanMetrics.uplinkSpeed); + out.writeInt(wanMetrics.downlinkLoad); + out.writeInt(wanMetrics.uplinkLoad); + out.writeInt(wanMetrics.lmd); + } + + if (connectionCapability == null) { + out.writeInt(0); + } else { + out.writeInt(connectionCapability.size()); + for (IpProtoPort ip : connectionCapability) { + out.writeInt(ip.proto); + out.writeInt(ip.port); + out.writeInt(ip.status); + } + } + if (osuProviderList == null) { out.writeInt(0); } else { @@ -254,18 +463,86 @@ public class WifiPasspointInfo implements Parcelable { @Override public WifiPasspointInfo createFromParcel(Parcel in) { WifiPasspointInfo p = new WifiPasspointInfo(); - p.bssid = (String) in.readValue(String.class.getClassLoader()); - p.venueName = (String) in.readValue(String.class.getClassLoader()); - p.networkAuthType = (String) in.readValue(String.class.getClassLoader()); - p.roamingConsortium = (String) in.readValue(String.class.getClassLoader()); - p.ipAddrTypeAvaibility = (String) in.readValue(String.class.getClassLoader()); - p.naiRealm = (String) in.readValue(String.class.getClassLoader()); - p.cellularNetwork = (String) in.readValue(String.class.getClassLoader()); - p.domainName = (String) in.readValue(String.class.getClassLoader()); - p.operatorFriendlyName = (String) in.readValue(String.class.getClassLoader()); - p.wanMetrics = (String) in.readValue(String.class.getClassLoader()); - p.connectionCapability = (String) in.readValue(String.class.getClassLoader()); - int n = in.readInt(); + int n; + + p.bssid = in.readString(); + p.venueName = in.readString(); + + n = in.readInt(); + if (n > 0) { + p.networkAuthType = new ArrayList<NetworkAuthType>(); + for (int i = 0; i < n; i++) { + NetworkAuthType auth = new NetworkAuthType(); + auth.type = in.readInt(); + auth.redirectUrl = in.readString(); + p.networkAuthType.add(auth); + } + } + + n = in.readInt(); + if (n > 0) { + p.roamingConsortium = new ArrayList<String>(); + for (int i = 0; i < n; i++) + p.roamingConsortium.add(in.readString()); + } + + n = in.readInt(); + if (n != IpAddressType.NULL_VALUE) { + p.ipAddrTypeAvailability = new IpAddressType(); + p.ipAddrTypeAvailability.availability = n; + } + + n = in.readInt(); + if (n > 0) { + p.naiRealm = new ArrayList<NaiRealm>(); + for (int i = 0; i < n; i++) { + NaiRealm realm = new NaiRealm(); + realm.encoding = in.readInt(); + realm.realm = in.readString(); + p.naiRealm.add(realm); + } + } + + n = in.readInt(); + if (n > 0) { + p.cellularNetwork = new CellularNetwork(); + p.cellularNetwork.rawData = new byte[n]; + in.readByteArray(p.cellularNetwork.rawData); + } + + n = in.readInt(); + if (n > 0) { + p.domainName = new ArrayList<String>(); + for (int i = 0; i < n; i++) + p.domainName.add(in.readString()); + } + + p.operatorFriendlyName = in.readString(); + + n = in.readInt(); + if (n > 0) { + p.wanMetrics = new WanMetrics(); + p.wanMetrics.wanInfo = in.readInt(); + p.wanMetrics.downlinkSpeed = in.readLong(); + p.wanMetrics.uplinkSpeed = in.readLong(); + p.wanMetrics.downlinkLoad = in.readInt(); + p.wanMetrics.uplinkLoad = in.readInt(); + p.wanMetrics.lmd = in.readInt(); + } + + n = in.readInt(); + if (n > 0) { + p.connectionCapability = new ArrayList<IpProtoPort>(); + for (int i = 0; i < n; i++) { + IpProtoPort ip = new IpProtoPort(); + ip.proto = in.readInt(); + ip.port = in.readInt(); + ip.status = in.readInt(); + p.connectionCapability.add(ip); + } + } + + n = in.readInt(); if (n > 0) { p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>(); for (int i = 0; i < n; i++) { @@ -274,6 +551,7 @@ public class WifiPasspointInfo implements Parcelable { p.osuProviderList.add(osu); } } + return p; } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java index 18a8f1e0c775..f40dc4f08594 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java @@ -94,10 +94,10 @@ public class WifiPasspointOsuProvider implements Parcelable { sb.append(" serverUri: ").append(serverUri); sb.append(" osuMethod: ").append(osuMethod); if (iconFileName != null) { - sb.append(" icon: [").append(iconWidth).append("x") + sb.append(" icon: <").append(iconWidth).append("x") .append(iconHeight).append(" ") .append(iconType).append(" ") - .append(iconFileName); + .append(iconFileName).append(">"); } if (osuNai != null) sb.append(" osuNai: ").append(osuNai); @@ -113,16 +113,16 @@ public class WifiPasspointOsuProvider implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeValue(ssid); - out.writeValue(friendlyName); - out.writeValue(serverUri); + out.writeString(ssid); + out.writeString(friendlyName); + out.writeString(serverUri); out.writeInt(osuMethod); out.writeInt(iconWidth); out.writeInt(iconHeight); - out.writeValue(iconType); - out.writeValue(iconFileName); - out.writeValue(osuNai); - out.writeValue(osuService); + out.writeString(iconType); + out.writeString(iconFileName); + out.writeString(osuNai); + out.writeString(osuService); // TODO: icon image? } @@ -131,16 +131,16 @@ public class WifiPasspointOsuProvider implements Parcelable { @Override public WifiPasspointOsuProvider createFromParcel(Parcel in) { WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider(); - osu.ssid = (String) in.readValue(String.class.getClassLoader()); - osu.friendlyName = (String) in.readValue(String.class.getClassLoader()); - osu.serverUri = (String) in.readValue(String.class.getClassLoader()); + osu.ssid = in.readString(); + osu.friendlyName = in.readString(); + osu.serverUri = in.readString(); osu.osuMethod = in.readInt(); osu.iconWidth = in.readInt(); osu.iconHeight = in.readInt(); - osu.iconType = (String) in.readValue(String.class.getClassLoader()); - osu.iconFileName = (String) in.readValue(String.class.getClassLoader()); - osu.osuNai = (String) in.readValue(String.class.getClassLoader()); - osu.osuService = (String) in.readValue(String.class.getClassLoader()); + osu.iconType = in.readString(); + osu.iconFileName = in.readString(); + osu.osuNai = in.readString(); + osu.osuService = in.readString(); return osu; } |