diff options
59 files changed, 1787 insertions, 811 deletions
diff --git a/api/current.txt b/api/current.txt index 3962492..d01edd4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8247,11 +8247,11 @@ package android.content.pm { } public static abstract interface LauncherApps.OnAppsChangedListener { - method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle); - method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle); - method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle); - method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean); - method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); + method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String); + method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String); + method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String); + method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean); + method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean); } public class PackageInfo implements android.os.Parcelable { @@ -16096,28 +16096,25 @@ package android.net { method public deprecated int getNetworkPreference(); method public static android.net.Network getProcessDefaultNetwork(); method public boolean isActiveNetworkMetered(); - method public boolean isNetworkActive(); + method public boolean isDefaultNetworkActive(); method public static boolean isNetworkTypeValid(int); - method public android.net.NetworkRequest listenForNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener); - method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); - method public void releaseNetworkRequest(android.net.NetworkRequest); + method public void registerDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public void reportBadNetwork(android.net.Network); - method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener); - method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.app.PendingIntent); + method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public deprecated boolean requestRouteToHost(int, int); method public deprecated void setNetworkPreference(int); method public static boolean setProcessDefaultNetwork(android.net.Network); method public deprecated int startUsingNetworkFeature(int, java.lang.String); method public deprecated int stopUsingNetworkFeature(int, java.lang.String); - method public void unregisterNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void unregisterDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback); field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo"; field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover"; field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; - field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork"; - field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES = "networkRequestNetworkCapabilities"; field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType"; field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity"; field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; @@ -16134,14 +16131,13 @@ package android.net { field public static final int TYPE_WIMAX = 6; // 0x6 } - public static class ConnectivityManager.NetworkCallbackListener { - ctor public ConnectivityManager.NetworkCallbackListener(); - method public void onAvailable(android.net.NetworkRequest, android.net.Network); - method public void onLinkPropertiesChanged(android.net.NetworkRequest, android.net.Network, android.net.LinkProperties); - method public void onLosing(android.net.NetworkRequest, android.net.Network, int); - method public void onLost(android.net.NetworkRequest, android.net.Network); - method public void onNetworkCapabilitiesChanged(android.net.NetworkRequest, android.net.Network, android.net.NetworkCapabilities); - method public void onReleased(android.net.NetworkRequest); + public static class ConnectivityManager.NetworkCallback { + ctor public ConnectivityManager.NetworkCallback(); + method public void onAvailable(android.net.Network); + method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities); + method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); + method public void onLosing(android.net.Network, int); + method public void onLost(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index bf9e0a7..2d8eed4 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -22,7 +22,7 @@ import android.os.ParcelUuid; * Callback definitions for interacting with BLE / GATT * @hide */ -interface IBluetoothGattCallback { +oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); @@ -63,7 +63,7 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - oneway void onAdvertiseStateChange(in int advertiseState, in int status); - oneway void onMultiAdvertiseCallback(in int status); + void onAdvertiseStateChange(in int advertiseState, in int status); + void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 04c0b9f..69fa408 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -67,7 +67,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that was removed. - * @hide remove before ship */ void onPackageRemoved(UserHandle user, String packageName); @@ -76,7 +75,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that was added. - * @hide remove before ship */ void onPackageAdded(UserHandle user, String packageName); @@ -85,7 +83,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that has changed. - * @hide remove before ship */ void onPackageChanged(UserHandle user, String packageName); @@ -99,7 +96,6 @@ public class LauncherApps { * available. * @param replacing Indicates whether these packages are replacing * existing ones. - * @hide remove before ship */ void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing); @@ -113,59 +109,9 @@ public class LauncherApps { * unavailable. * @param replacing Indicates whether the packages are about to be * replaced with new versions. - * @hide remove before ship */ void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing); - /** - * Indicates that a package was removed from the specified profile. - * - * @param packageName The name of the package that was removed. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageRemoved(String packageName, UserHandle user); - - /** - * Indicates that a package was added to the specified profile. - * - * @param packageName The name of the package that was added. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageAdded(String packageName, UserHandle user); - - /** - * Indicates that a package was modified in the specified profile. - * - * @param packageName The name of the package that has changed. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageChanged(String packageName, UserHandle user); - - /** - * Indicates that one or more packages have become available. For - * example, this can happen when a removable storage card has - * reappeared. - * - * @param packageNames The names of the packages that have become - * available. - * @param user The UserHandle of the profile that generated the change. - * @param replacing Indicates whether these packages are replacing - * existing ones. - */ - void onPackagesAvailable(String [] packageNames, UserHandle user, boolean replacing); - - /** - * Indicates that one or more packages have become unavailable. For - * example, this can happen when a removable storage card has been - * removed. - * - * @param packageNames The names of the packages that have become - * unavailable. - * @param user The UserHandle of the profile that generated the change. - * @param replacing Indicates whether the packages are about to be - * replaced with new versions. - */ - void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing); } /** @hide */ @@ -361,8 +307,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageRemoved(user, packageName); // TODO: Remove before ship - listener.onPackageRemoved(packageName, user); + listener.onPackageRemoved(user, packageName); } } } @@ -374,8 +319,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageChanged(user, packageName); // TODO: Remove before ship - listener.onPackageChanged(packageName, user); + listener.onPackageChanged(user, packageName); } } } @@ -387,8 +331,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageAdded(user, packageName); // TODO: Remove before ship - listener.onPackageAdded(packageName, user); + listener.onPackageAdded(user, packageName); } } } @@ -401,8 +344,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackagesAvailable(user, packageNames, replacing); // TODO: Remove - listener.onPackagesAvailable(packageNames, user, replacing); + listener.onPackagesAvailable(user, packageNames, replacing); } } } @@ -415,8 +357,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackagesUnavailable(user, packageNames, replacing); // TODO: Remove - listener.onPackagesUnavailable(packageNames, user, replacing); + listener.onPackagesUnavailable(user, packageNames, replacing); } } } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 68f4d64..77d0c41 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -190,22 +190,18 @@ public abstract class CameraDevice implements AutoCloseable { * Then obtain the Surface with * {@link android.renderscript.Allocation#getSurface}.</li> * - * <li>For access to raw, uncompressed or JPEG data in the application: Create a - * {@link android.media.ImageReader} object with the one of the supported - * {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a - * size from the supported - * {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain - * a Surface from it with {@link android.media.ImageReader#getSurface}.</li> + * <li>For access to raw, uncompressed JPEG data in the application: Create an + * {@link android.media.ImageReader} object with one of the supported output formats given by + * {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the + * corresponding supported sizes by passing the chosen output format into + * {@link StreamConfigurationMap#getOutputSizes(int)}. Then obtain a + * {@link android.view.Surface} from it with {@link android.media.ImageReader#getSurface()}. + * </li> * * </ul> * - * </p> - * * <p>The camera device will query each Surface's size and formats upon this - * call, so they must be set to a valid setting at this time (in particular: - * if the format is user-visible, it must be one of - * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of - * {@link StreamConfigurationMap#getOutputSizes(int)}).</p> + * call, so they must be set to a valid setting at this time.</p> * * <p>It can take several hundred milliseconds for the session's configuration to complete, * since camera hardware may need to be powered on or reconfigured. Once the configuration is diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.aidl b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl new file mode 100644 index 0000000..157b5b3 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl @@ -0,0 +1,19 @@ +/* + * 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.hardware.hdmi; + +parcelable HdmiPortInfo; diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java new file mode 100644 index 0000000..7b25f8a --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java @@ -0,0 +1,164 @@ +/* + * 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.hardware.hdmi; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class to encapsulate HDMI port information. Contains the capability of the ports such as + * HDMI-CEC, MHL, ARC(Audio Return Channel), and physical address assigned to each port. + * + * @hide + */ +@SystemApi +public final class HdmiPortInfo implements Parcelable { + /** HDMI port type: Input */ + public static final int PORT_INPUT = 0; + + /** HDMI port type: Output */ + public static final int PORT_OUTPUT = 1; + + private final int mId; + private final int mType; + private final int mAddress; + private final boolean mCecSupported; + private final boolean mArcSupported; + private final boolean mMhlSupported; + + /** + * Constructor. + * + * @param id identifier assigned to each port. 1 for HDMI port 1 + * @param type HDMI port input/output type + * @param address physical address of the port + * @param cec {@code true} if HDMI-CEC is supported on the port + * @param mhl {@code true} if MHL is supported on the port + * @param arc {@code true} if audio return channel is supported on the port + */ + public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) { + mId = id; + mType = type; + mAddress = address; + mCecSupported = cec; + mArcSupported = arc; + mMhlSupported = mhl; + } + + /** + * Returns the port id. + * + * @return port id + */ + public int getId() { + return mId; + } + + /** + * Returns the port type. + * + * @return port type + */ + public int getType() { + return mType; + } + + /** + * Returns the port address. + * + * @return port address + */ + public int getAddress() { + return mAddress; + } + + /** + * Returns {@code true} if the port supports HDMI-CEC signaling. + * + * @return {@code true} if the port supports HDMI-CEC signaling. + */ + public boolean isCecSupported() { + return mCecSupported; + } + + /** + * Returns {@code true} if the port supports MHL signaling. + * + * @return {@code true} if the port supports MHL signaling. + */ + public boolean isMhlSupported() { + return mMhlSupported; + } + + /** + * Returns {@code true} if the port supports audio return channel. + * + * @return {@code true} if the port supports audio return channel + */ + public boolean isArcSupported() { + return mArcSupported; + } + + /** + * Describe the kinds of special objects contained in this Parcelable's + * marshalled representation. + */ + @Override + public int describeContents() { + return 0; + } + + + /** + * A helper class to deserialize {@link HdmiPortInfo} for a parcel. + */ + public static final Parcelable.Creator<HdmiPortInfo> CREATOR = + new Parcelable.Creator<HdmiPortInfo>() { + @Override + public HdmiPortInfo createFromParcel(Parcel source) { + int id = source.readInt(); + int type = source.readInt(); + int address = source.readInt(); + boolean cec = (source.readInt() == 1); + boolean arc = (source.readInt() == 1); + boolean mhl = (source.readInt() == 1); + return new HdmiPortInfo(id, type, address, cec, arc, mhl); + } + + @Override + public HdmiPortInfo[] newArray(int size) { + return new HdmiPortInfo[size]; + } + }; + + /** + * Serialize this object into a {@link Parcel}. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeInt(mType); + dest.writeInt(mAddress); + dest.writeInt(mCecSupported ? 1 : 0); + dest.writeInt(mArcSupported ? 1 : 0); + dest.writeInt(mMhlSupported ? 1 : 0); + } +} diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 6dc4a4f..85af3d1 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -16,6 +16,8 @@ package android.hardware.hdmi; import android.annotation.SystemApi; +import android.os.RemoteException; +import android.util.Log; /** * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system @@ -33,4 +35,46 @@ public final class HdmiTvClient { HdmiTvClient(IHdmiControlService service) { mService = service; } + + // Factory method for HdmiTvClient. + // Declared package-private. Accessed by HdmiControlManager only. + static HdmiTvClient create(IHdmiControlService service) { + return new HdmiTvClient(service); + } + + /** + * Callback interface used to get the result of {@link #deviceSelect}. + */ + public interface SelectCallback { + /** + * Called when the operation is finished. + * + * @param result the result value of {@link #deviceSelect} + */ + void onComplete(int result); + } + + /** + * Select a CEC logical device to be a new active source. + * + * @param logicalAddress + * @param callback + */ + public void deviceSelect(int logicalAddress, SelectCallback callback) { + // TODO: Replace SelectCallback with PartialResult. + try { + mService.deviceSelect(logicalAddress, getCallbackWrapper(callback)); + } catch (RemoteException e) { + Log.e(TAG, "failed to select device: ", e); + } + } + + private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) { + return new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + callback.onComplete(result); + } + }; + } } diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 8da38e1..8d7c638 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -33,4 +33,5 @@ interface IHdmiControlService { void queryDisplayStatus(IHdmiControlCallback callback); void addHotplugEventListener(IHdmiHotplugEventListener listener); void removeHotplugEventListener(IHdmiHotplugEventListener listener); + void deviceSelect(int logicalAddress, IHdmiControlCallback callback); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index ff90e78..ba31243 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -868,10 +868,10 @@ public class ConnectivityManager { return -1; } - NetworkRequest request = removeRequestForFeature(netCap); - if (request != null) { + NetworkCallback networkCallback = removeRequestForFeature(netCap); + if (networkCallback != null) { Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature); - releaseNetworkRequest(request); + unregisterNetworkCallback(networkCallback); } return 1; } @@ -982,15 +982,15 @@ public class ConnectivityManager { int expireSequenceNumber; Network currentNetwork; int delay = -1; - NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() { + NetworkCallback networkCallback = new NetworkCallback() { @Override - public void onAvailable(NetworkRequest request, Network network) { + public void onAvailable(Network network) { currentNetwork = network; Log.d(TAG, "startUsingNetworkFeature got Network:" + network); setProcessDefaultNetworkForHostResolution(network); } @Override - public void onLost(NetworkRequest request, Network network) { + public void onLost(Network network) { if (network.equals(currentNetwork)) { currentNetwork = null; setProcessDefaultNetworkForHostResolution(null); @@ -1024,7 +1024,7 @@ public class ConnectivityManager { if (l == null) return; ourSeqNum = l.expireSequenceNumber; if (l.expireSequenceNumber == sequenceNum) { - releaseNetworkRequest(l.networkRequest); + unregisterNetworkCallback(l.networkCallback); sLegacyRequests.remove(netCap); } } @@ -1041,7 +1041,7 @@ public class ConnectivityManager { l.networkCapabilities = netCap; l.delay = delay; l.expireSequenceNumber = 0; - l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0, + l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0, REQUEST, type); if (l.networkRequest == null) return null; sLegacyRequests.put(netCap, l); @@ -1057,11 +1057,11 @@ public class ConnectivityManager { } } - private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) { + private NetworkCallback removeRequestForFeature(NetworkCapabilities netCap) { synchronized (sLegacyRequests) { LegacyRequest l = sLegacyRequests.remove(netCap); if (l == null) return null; - return l.networkRequest; + return l.networkCallback; } } @@ -1184,8 +1184,8 @@ public class ConnectivityManager { } /** - * Callback for use with {@link ConnectivityManager#registerNetworkActiveListener} to - * find out when the current network has gone in to a high power state. + * Callback for use with {@link ConnectivityManager#registerDefaultNetworkActiveListener} + * to find out when the system default network has gone in to a high power state. */ public interface OnNetworkActiveListener { /** @@ -1194,7 +1194,7 @@ public class ConnectivityManager { * operations. Note that this listener only tells you when the network becomes * active; if at any other time you want to know whether it is active (and thus okay * to initiate network traffic), you can retrieve its instantaneous state with - * {@link ConnectivityManager#isNetworkActive}. + * {@link ConnectivityManager#isDefaultNetworkActive}. */ public void onNetworkActive(); } @@ -1215,13 +1215,18 @@ public class ConnectivityManager { = new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>(); /** - * Start listening to reports when the data network is active, meaning it is - * a good time to perform network traffic. Use {@link #isNetworkActive()} - * to determine the current state of the network after registering the listener. + * Start listening to reports when the system's default data network is active, meaning it is + * a good time to perform network traffic. Use {@link #isDefaultNetworkActive()} + * to determine the current state of the system's default network after registering the + * listener. + * <p> + * If the process default network has been set with + * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not + * reflect the process's default, but the system default. * * @param l The listener to be told when the network is active. */ - public void registerNetworkActiveListener(final OnNetworkActiveListener l) { + public void registerDefaultNetworkActiveListener(final OnNetworkActiveListener l) { INetworkActivityListener rl = new INetworkActivityListener.Stub() { @Override public void onNetworkActive() throws RemoteException { @@ -1238,11 +1243,11 @@ public class ConnectivityManager { /** * Remove network active listener previously registered with - * {@link #registerNetworkActiveListener}. + * {@link #registerDefaultNetworkActiveListener}. * * @param l Previously registered listener. */ - public void unregisterNetworkActiveListener(OnNetworkActiveListener l) { + public void unregisterDefaultNetworkActiveListener(OnNetworkActiveListener l) { INetworkActivityListener rl = mNetworkActivityListeners.get(l); if (rl == null) { throw new IllegalArgumentException("Listener not registered: " + l); @@ -1261,7 +1266,7 @@ public class ConnectivityManager { * this state. This method tells you whether right now is currently a good time to * initiate network traffic, as the network is already active. */ - public boolean isNetworkActive() { + public boolean isDefaultNetworkActive() { try { return getNetworkManagementService().isNetworkActive(); } catch (RemoteException e) { @@ -1893,7 +1898,7 @@ public class ConnectivityManager { * Base class for NetworkRequest callbacks. Used for notifications about network * changes. Should be extended by applications wanting notifications. */ - public static class NetworkCallbackListener { + public static class NetworkCallback { /** @hide */ public static final int PRECHECK = 1; /** @hide */ @@ -1916,78 +1921,68 @@ public class ConnectivityManager { * Called whenever the framework connects to a network that it may use to * satisfy this request */ - public void onPreCheck(NetworkRequest networkRequest, Network network) {} + public void onPreCheck(Network network) {} /** * Called when the framework connects and has declared new network ready for use. + * This callback may be called more than once if the {@link Network} that is + * satisfying the request changes. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} of the satisfying network. */ - public void onAvailable(NetworkRequest networkRequest, Network network) {} + public void onAvailable(Network network) {} /** * Called when the network is about to be disconnected. Often paired with an - * {@link NetworkCallbackListener#onAvailable} call with the new replacement network + * {@link NetworkCallback#onAvailable} call with the new replacement network * for graceful handover. This may not be called if we have a hard loss * (loss without warning). This may be followed by either a - * {@link NetworkCallbackListener#onLost} call or a - * {@link NetworkCallbackListener#onAvailable} call for this network depending + * {@link NetworkCallback#onLost} call or a + * {@link NetworkCallback#onAvailable} call for this network depending * on whether we lose or regain it. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. - * @param network The {@link Network} of the failing network. - * @param maxSecToLive The time in seconds the framework will attempt to keep the - * network connected. Note that the network may suffers a + * @param network The {@link Network} that is about to be disconnected. + * @param maxMsToLive The time in ms the framework will attempt to keep the + * network connected. Note that the network may suffer a * hard loss at any time. */ - public void onLosing(NetworkRequest networkRequest, Network network, int maxSecToLive) {} + public void onLosing(Network network, int maxMsToLive) {} /** * Called when the framework has a hard loss of the network or when the * graceful failure ends. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} lost. */ - public void onLost(NetworkRequest networkRequest, Network network) {} + public void onLost(Network network) {} /** * Called if no network is found in the given timeout time. If no timeout is given, * this will not be called. * @hide */ - public void onUnavailable(NetworkRequest networkRequest) {} + public void onUnavailable() {} /** * Called when the network the framework connected to for this request * changes capabilities but still satisfies the stated need. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} whose capabilities have changed. * @param networkCapabilities The new {@link NetworkCapabilities} for this network. */ - public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network, + public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {} /** * Called when the network the framework connected to for this request * changes {@link LinkProperties}. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} whose link properties have changed. * @param linkProperties The new {@link LinkProperties} for this network. */ - public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network, - LinkProperties linkProperties) {} + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {} - /** - * Called when a {@link #releaseNetworkRequest} call concludes and the registered - * callbacks will no longer be used. - * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. - */ - public void onReleased(NetworkRequest networkRequest) {} + private NetworkRequest networkRequest; } private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER; @@ -2013,12 +2008,12 @@ public class ConnectivityManager { private static final int EXPIRE_LEGACY_REQUEST = BASE + 10; private class CallbackHandler extends Handler { - private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap; + private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap; private final AtomicInteger mRefCount; private static final String TAG = "ConnectivityManager.CallbackHandler"; private final ConnectivityManager mCm; - CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbackListener>callbackMap, + CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap, AtomicInteger refCount, ConnectivityManager cm) { super(looper); mCallbackMap = callbackMap; @@ -2032,9 +2027,9 @@ public class ConnectivityManager { switch (message.what) { case CALLBACK_PRECHECK: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onPreCheck(request, getNetwork(message)); + callbacks.onPreCheck(getNetwork(message)); } else { Log.e(TAG, "callback not found for PRECHECK message"); } @@ -2042,9 +2037,9 @@ public class ConnectivityManager { } case CALLBACK_AVAILABLE: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onAvailable(request, getNetwork(message)); + callbacks.onAvailable(getNetwork(message)); } else { Log.e(TAG, "callback not found for AVAILABLE message"); } @@ -2052,9 +2047,9 @@ public class ConnectivityManager { } case CALLBACK_LOSING: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onLosing(request, getNetwork(message), message.arg1); + callbacks.onLosing(getNetwork(message), message.arg1); } else { Log.e(TAG, "callback not found for LOSING message"); } @@ -2062,9 +2057,9 @@ public class ConnectivityManager { } case CALLBACK_LOST: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onLost(request, getNetwork(message)); + callbacks.onLost(getNetwork(message)); } else { Log.e(TAG, "callback not found for LOST message"); } @@ -2072,12 +2067,12 @@ public class ConnectivityManager { } case CALLBACK_UNAVAIL: { NetworkRequest req = (NetworkRequest)message.obj; - NetworkCallbackListener callbacks = null; + NetworkCallback callbacks = null; synchronized(mCallbackMap) { callbacks = mCallbackMap.get(req); } if (callbacks != null) { - callbacks.onUnavailable(req); + callbacks.onUnavailable(); } else { Log.e(TAG, "callback not found for UNAVAIL message"); } @@ -2085,12 +2080,12 @@ public class ConnectivityManager { } case CALLBACK_CAP_CHANGED: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { Network network = getNetwork(message); NetworkCapabilities cap = mCm.getNetworkCapabilities(network); - callbacks.onNetworkCapabilitiesChanged(request, network, cap); + callbacks.onCapabilitiesChanged(network, cap); } else { Log.e(TAG, "callback not found for CHANGED message"); } @@ -2098,12 +2093,12 @@ public class ConnectivityManager { } case CALLBACK_IP_CHANGED: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { Network network = getNetwork(message); LinkProperties lp = mCm.getLinkProperties(network); - callbacks.onLinkPropertiesChanged(request, network, lp); + callbacks.onLinkPropertiesChanged(network, lp); } else { Log.e(TAG, "callback not found for CHANGED message"); } @@ -2111,20 +2106,19 @@ public class ConnectivityManager { } case CALLBACK_RELEASED: { NetworkRequest req = (NetworkRequest)message.obj; - NetworkCallbackListener callbacks = null; + NetworkCallback callbacks = null; synchronized(mCallbackMap) { callbacks = mCallbackMap.remove(req); } if (callbacks != null) { - callbacks.onReleased(req); + synchronized(mRefCount) { + if (mRefCount.decrementAndGet() == 0) { + getLooper().quit(); + } + } } else { Log.e(TAG, "callback not found for CANCELED message"); } - synchronized(mRefCount) { - if (mRefCount.decrementAndGet() == 0) { - getLooper().quit(); - } - } break; } case CALLBACK_EXIT: { @@ -2142,7 +2136,7 @@ public class ConnectivityManager { private NetworkRequest getNetworkRequest(Message msg) { return (NetworkRequest)(msg.obj); } - private NetworkCallbackListener getCallbacks(NetworkRequest req) { + private NetworkCallback getCallbacks(NetworkRequest req) { synchronized(mCallbackMap) { return mCallbackMap.get(req); } @@ -2150,7 +2144,7 @@ public class ConnectivityManager { private Network getNetwork(Message msg) { return new Network(msg.arg2); } - private NetworkCallbackListener removeCallbacks(Message msg) { + private NetworkCallback removeCallbacks(Message msg) { NetworkRequest req = (NetworkRequest)msg.obj; synchronized(mCallbackMap) { return mCallbackMap.remove(req); @@ -2158,19 +2152,19 @@ public class ConnectivityManager { } } - private void addCallbackListener() { + private void incCallbackHandlerRefCount() { synchronized(sCallbackRefCount) { if (sCallbackRefCount.incrementAndGet() == 1) { // TODO - switch this over to a ManagerThread or expire it when done HandlerThread callbackThread = new HandlerThread("ConnectivityManager"); callbackThread.start(); sCallbackHandler = new CallbackHandler(callbackThread.getLooper(), - sNetworkCallbackListener, sCallbackRefCount, this); + sNetworkCallback, sCallbackRefCount, this); } } } - private void removeCallbackListener() { + private void decCallbackHandlerRefCount() { synchronized(sCallbackRefCount) { if (sCallbackRefCount.decrementAndGet() == 0) { sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget(); @@ -2179,8 +2173,8 @@ public class ConnectivityManager { } } - static final HashMap<NetworkRequest, NetworkCallbackListener> sNetworkCallbackListener = - new HashMap<NetworkRequest, NetworkCallbackListener>(); + static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback = + new HashMap<NetworkRequest, NetworkCallback>(); static final AtomicInteger sCallbackRefCount = new AtomicInteger(0); static CallbackHandler sCallbackHandler = null; @@ -2188,51 +2182,48 @@ public class ConnectivityManager { private final static int REQUEST = 2; private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec, int action, + NetworkCallback networkCallback, int timeoutSec, int action, int legacyType) { - NetworkRequest networkRequest = null; - if (networkCallbackListener == null) { - throw new IllegalArgumentException("null NetworkCallbackListener"); + if (networkCallback == null) { + throw new IllegalArgumentException("null NetworkCallback"); } if (need == null) throw new IllegalArgumentException("null NetworkCapabilities"); try { - addCallbackListener(); + incCallbackHandlerRefCount(); if (action == LISTEN) { - networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler), - new Binder()); + networkCallback.networkRequest = mService.listenForNetwork(need, + new Messenger(sCallbackHandler), new Binder()); } else { - networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), - timeoutSec, new Binder(), legacyType); + networkCallback.networkRequest = mService.requestNetwork(need, + new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); } - if (networkRequest != null) { - synchronized(sNetworkCallbackListener) { - sNetworkCallbackListener.put(networkRequest, networkCallbackListener); + if (networkCallback.networkRequest != null) { + synchronized(sNetworkCallback) { + sNetworkCallback.put(networkCallback.networkRequest, networkCallback); } } } catch (RemoteException e) {} - if (networkRequest == null) removeCallbackListener(); - return networkRequest; + if (networkCallback.networkRequest == null) decCallbackHandlerRefCount(); + return networkCallback.networkRequest; } /** * Request a network to satisfy a set of {@link NetworkCapabilities}. * * This {@link NetworkRequest} will live until released via - * {@link #releaseNetworkRequest} or the calling application exits. + * {@link #unregisterNetworkCallback} or the calling application exits. * Status of the request can be followed by listening to the various - * callbacks described in {@link NetworkCallbackListener}. The {@link Network} + * callbacks described in {@link NetworkCallback}. The {@link Network} * can be used to direct traffic to the network. * - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The {@link NetworkCallbackListener} to be utilized for this - * request. Note the callbacks can be shared by multiple - * requests and the NetworkRequest token utilized to - * determine to which request the callback relates. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this + * request. Note the callback must not be shared - they + * uniquely specify this request. */ - public NetworkRequest requestNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener) { - return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE); + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, + REQUEST, TYPE_NONE); } /** @@ -2240,53 +2231,53 @@ public class ConnectivityManager { * by a timeout. * * This function behaves identically to the non-timedout version, but if a suitable - * network is not found within the given time (in Seconds) the - * {@link NetworkCallbackListener#unavailable} callback is called. The request must + * network is not found within the given time (in milliseconds) the + * {@link NetworkCallback#unavailable} callback is called. The request must * still be released normally by calling {@link releaseNetworkRequest}. - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The callbacks to be utilized for this request. Note - * the callbacks can be shared by multiple requests and - * the NetworkRequest token utilized to determine to which - * request the callback relates. - * @param timeoutSec The time in seconds to attempt looking for a suitable network - * before {@link NetworkCallbackListener#unavailable} is called. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The callbacks to be utilized for this request. Note + * the callbacks must not be shared - they uniquely specify + * this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#unavailable} is called. * @hide */ - public NetworkRequest requestNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec) { - return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST, - TYPE_NONE); + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, + int timeoutMs) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, + REQUEST, TYPE_NONE); } /** - * The maximum number of seconds the framework will look for a suitable network + * The maximum number of milliseconds the framework will look for a suitable network * during a timeout-equiped call to {@link requestNetwork}. * {@hide} */ - public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60; + public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000; /** * The lookup key for a {@link Network} object included with the intent after * succesfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * @hide */ public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork"; /** - * The lookup key for a {@link NetworkCapabilities} object included with the intent after + * The lookup key for a {@link NetworkRequest} object included with the intent after * succesfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * @hide */ - public static final String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES = - "networkRequestNetworkCapabilities"; + public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = + "networkRequestNetworkRequest"; /** * Request a network to satisfy a set of {@link NetworkCapabilities}. * - * This function behavies identically to the callback-equiped version, but instead - * of {@link NetworkCallbackListener} a {@link PendingIntent} is used. This means + * This function behavies identically to the version that takes a NetworkCallback, but instead + * of {@link NetworkCallback} a {@link PendingIntent} is used. This means * the request may outlive the calling application and get called back when a suitable * network is found. * <p> @@ -2295,10 +2286,10 @@ public class ConnectivityManager { * <receiver> tag in an AndroidManifest.xml file * <p> * The operation Intent is delivered with two extras, a {@link Network} typed - * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities} - * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES} containing + * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest} + * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing * the original requests parameters. It is important to create a new, - * {@link NetworkCallbackListener} based request before completing the processing of the + * {@link NetworkCallback} based request before completing the processing of the * Intent to reserve the network or it will be released shortly after the Intent * is processed. * <p> @@ -2306,51 +2297,49 @@ public class ConnectivityManager { * two Intents defined by {@link Intent#filterEquals}), then it will be removed and * replaced by this one, effectively releasing the previous {@link NetworkRequest}. * <p> - * The request may be released normally by calling {@link #releaseNetworkRequest}. + * The request may be released normally by calling {@link #unregisterNetworkCallback}. * - * @param need {@link NetworkCapabilities} required by this request. + * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds - * to the {@link NetworkCallbackListener#onAvailable} call. Typically + * to the {@link NetworkCallback#onAvailable} call. Typically * comes from {@link PendingIntent#getBroadcast}. - * @return A {@link NetworkRequest} object identifying the request. + * @hide */ - public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) { + public void requestNetwork(NetworkRequest request, PendingIntent operation) { try { - return mService.pendingRequestForNetwork(need, operation); + mService.pendingRequestForNetwork(request.networkCapabilities, operation); } catch (RemoteException e) {} - return null; } /** * Registers to receive notifications about all networks which satisfy the given - * {@link NetworkCapabilities}. The callbacks will continue to be called until - * either the application exits or the request is released using - * {@link #releaseNetworkRequest}. + * {@link NetworkRequest}. The callbacks will continue to be called until + * either the application exits or {@link #unregisterNetworkCallback} is called * - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The {@link NetworkCallbackListener} to be called as suitable - * networks change state. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} that the system will call as suitable + * networks change state. */ - public NetworkRequest listenForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener) { - return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE); + public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE); } /** - * Releases a {@link NetworkRequest} generated either through a {@link #requestNetwork} - * or a {@link #listenForNetwork} call. The {@link NetworkCallbackListener} given in the - * earlier call may continue receiving calls until the - * {@link NetworkCallbackListener#onReleased} function is called, signifying the end - * of the request. + * Unregisters callbacks about and possibly releases networks originating from + * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the + * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork}, + * any networks that had been connected to only to satisfy that request will be + * disconnected. * - * @param networkRequest The {@link NetworkRequest} generated by an earlier call to - * {@link #requestNetwork} or {@link #listenForNetwork}. + * @param networkCallback The {@link NetworkCallback} used when making the request. */ - public void releaseNetworkRequest(NetworkRequest networkRequest) { - if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest"); + public void unregisterNetworkCallback(NetworkCallback networkCallback) { + if (networkCallback == null || networkCallback.networkRequest == null || + networkCallback.networkRequest.requestId == REQUEST_ID_UNSET) { + throw new IllegalArgumentException("Invalid NetworkCallback"); + } try { - mService.releaseNetworkRequest(networkRequest); + mService.releaseNetworkRequest(networkCallback.networkRequest); } catch (RemoteException e) {} } diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index d933f26..318aabe 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -29,8 +29,9 @@ import javax.net.SocketFactory; /** * Identifies a {@code Network}. This is supplied to applications via - * {@link ConnectivityManager.NetworkCallbackListener} in response to - * {@link ConnectivityManager#requestNetwork} or {@link ConnectivityManager#listenForNetwork}. + * {@link ConnectivityManager.NetworkCallback} in response to the active + * {@link ConnectivityManager#requestNetwork} or passive + * {@link ConnectivityManager#registerNetworkCallback} calls. * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis * through a targeted {@link SocketFactory} or process-wide via * {@link ConnectivityManager#setProcessDefaultNetwork}. diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 7911c72..36dc573 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Defines a request for a network, made through {@link NetworkRequest.Builder} and used * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes - * via {@link ConnectivityManager#listenForNetwork}. + * via {@link ConnectivityManager#registerNetworkCallback}. */ public class NetworkRequest implements Parcelable { /** diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 7b3dc84..1d2f1bf 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -46,7 +46,13 @@ public final class WindowInsets { * since it would allow them to inadvertently consume unknown insets by returning it. * @hide */ - public static final WindowInsets EMPTY = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + public static final WindowInsets CONSUMED; + + static { + CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + CONSUMED.mSystemWindowInsetsConsumed = true; + CONSUMED.mWindowDecorInsetsConsumed = true; + } /** @hide */ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) { diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 2b4677c..d426edc 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -605,21 +605,21 @@ public interface WindowManagerPolicy { public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation); /** - * Return whether the given window should forcibly hide everything - * behind it. Typically returns true for the keyguard. + * Return whether the given window is forcibly hiding all windows except windows with + * FLAG_SHOW_WHEN_LOCKED set. Typically returns true for the keyguard. */ - public boolean doesForceHide(WindowManager.LayoutParams attrs); + public boolean isForceHiding(WindowManager.LayoutParams attrs); /** - * Return whether the given window can become one that passes doesForceHide() test. + * Return whether the given window can become one that passes isForceHiding() test. * Typically returns true for the StatusBar. */ public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs); /** * Determine if a window that is behind one that is force hiding - * (as determined by {@link #doesForceHide}) should actually be hidden. + * (as determined by {@link #isForceHiding}) should actually be hidden. * For example, typically returns false for the status bar. Be careful * to return false for any window that you may hide yourself, since this * will conflict with what you set. @@ -830,13 +830,11 @@ public interface WindowManagerPolicy { * setting the window's frame, either here or in finishLayout(). * * @param win The window being positioned. - * @param attrs The LayoutParams of the window. * @param attached For sub-windows, the window it is attached to; this * window will already have had layoutWindow() called on it * so you can use its Rect. Otherwise null. */ - public void layoutWindowLw(WindowState win, - WindowManager.LayoutParams attrs, WindowState attached); + public void layoutWindowLw(WindowState win, WindowState attached); /** diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index 8a9cb22..ea36e37 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; @@ -246,6 +247,13 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar } @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + init(getContext()); + requestApplyInsets(); + } + + @Override public void onWindowSystemUiVisibilityChanged(int visible) { super.onWindowSystemUiVisibilityChanged(visible); pullChildren(); @@ -329,7 +337,7 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar // insets in all cases, we need to know the measured size of the various action // bar elements. onApplyWindowInsets() happens before the measure pass, so we can't // do that here. Instead we will take this up in onMeasure(). - return WindowInsets.EMPTY; + return WindowInsets.CONSUMED; } @Override diff --git a/core/res/res/anim-land/task_close_enter.xml b/core/res/res/anim-land/task_close_enter.xml deleted file mode 100644 index facc42b..0000000 --- a/core/res/res/anim-land/task_close_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400"/> - - <translate android:fromXDelta="-120%" android:toXDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="300" - android:duration="400" /> - - <scale android:fromXScale=".5" android:toXScale="1.0" - android:fromYScale=".5" android:toYScale="1.0" - android:pivotY="50%p" android:pivotX="0%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_close_exit.xml b/core/res/res/anim-land/task_close_exit.xml deleted file mode 100644 index e104c33..0000000 --- a/core/res/res/anim-land/task_close_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300"/> - - <translate android:fromXDelta="0" android:toXDelta="120%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="300"/> - - <scale android:fromXScale="1.0" android:toXScale="0.5" - android:fromYScale="1.0" android:toYScale="0.5" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotY="50%p" android:pivotX="100%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="700" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_open_enter.xml b/core/res/res/anim-land/task_open_enter.xml deleted file mode 100644 index dc7c7a9..0000000 --- a/core/res/res/anim-land/task_open_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400"/> - - <translate android:fromXDelta="120%" android:toXDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="300" - android:duration="400" /> - - <scale android:fromXScale=".5" android:toXScale="1.0" - android:fromYScale=".5" android:toYScale="1.0" - android:pivotY="50%p" android:pivotX="100%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_open_exit.xml b/core/res/res/anim-land/task_open_exit.xml deleted file mode 100644 index 701afa6..0000000 --- a/core/res/res/anim-land/task_open_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300"/> - - <translate android:fromXDelta="0" android:toXDelta="-120%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="300"/> - - <scale android:fromXScale="1.0" android:toXScale="0.5" - android:fromYScale="1.0" android:toYScale="0.5" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotY="50%p" android:pivotX="0%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="700" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_close_enter.xml b/core/res/res/anim-sw720dp/task_close_enter.xml deleted file mode 100644 index e25978b..0000000 --- a/core/res/res/anim-sw720dp/task_close_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400"/> - - <translate android:fromYDelta="-50%" android:toYDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="500" - android:duration="400" /> - - <scale android:fromXScale=".8" android:toXScale="1.0" - android:fromYScale=".8" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="0%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_close_exit.xml b/core/res/res/anim-sw720dp/task_close_exit.xml deleted file mode 100644 index 2d7e2a6..0000000 --- a/core/res/res/anim-sw720dp/task_close_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350"/> - - <translate android:fromYDelta="0" android:toYDelta="50%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="350"/> - - <scale android:fromXScale="1.0" android:toXScale="0.8" - android:fromYScale="1.0" android:toYScale="0.8" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="100%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350" /> - - <!-- This is needed to keep the animation running while task_close_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="900" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_open_enter.xml b/core/res/res/anim-sw720dp/task_open_enter.xml deleted file mode 100644 index d583b6e..0000000 --- a/core/res/res/anim-sw720dp/task_open_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400"/> - - <translate android:fromYDelta="50%" android:toYDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="500" - android:duration="400" /> - - <scale android:fromXScale=".8" android:toXScale="1.0" - android:fromYScale=".8" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="100%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_open_exit.xml b/core/res/res/anim-sw720dp/task_open_exit.xml deleted file mode 100644 index dd25a0c..0000000 --- a/core/res/res/anim-sw720dp/task_open_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350"/> - - <translate android:fromYDelta="0" android:toYDelta="-50%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="350"/> - - <scale android:fromXScale="1.0" android:toXScale="0.8" - android:fromYScale="1.0" android:toYScale="0.8" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="0%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="900" /> -</set>
\ No newline at end of file diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f1d9dc3..0ddfeb6 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -460,7 +460,7 @@ <string name="user_owner_label">Personal apps</string> <!-- Label for a corporate profile in the intent forwarding app. --> - <string name="managed_profile_label">Android for Work</string> + <string name="managed_profile_label">Android Work</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_costMoney">Services that cost you money</string> diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 79fe4d3..ef3d0d7 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -39,6 +39,7 @@ namespace uirenderer { TessellationCache::Description::Description() : type(kNone) + , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) , strokeWidth(1.0f) { @@ -47,6 +48,7 @@ TessellationCache::Description::Description() TessellationCache::Description::Description(Type type) : type(type) + , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) , strokeWidth(1.0f) { @@ -55,6 +57,7 @@ TessellationCache::Description::Description(Type type) TessellationCache::Description::Description(Type type, const SkPaint* paint) : type(type) + , aa(paint->isAntiAlias()) , cap(paint->getStrokeCap()) , style(paint->getStyle()) , strokeWidth(paint->getStrokeWidth()) { @@ -63,6 +66,7 @@ TessellationCache::Description::Description(Type type, const SkPaint* paint) hash_t TessellationCache::Description::hash() const { uint32_t hash = JenkinsHashMix(0, type); + hash = JenkinsHashMix(hash, aa); hash = JenkinsHashMix(hash, cap); hash = JenkinsHashMix(hash, style); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 8f37230..d4ff943 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -55,6 +55,7 @@ public: }; Type type; + bool aa; SkPaint::Cap cap; SkPaint::Style style; float strokeWidth; diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 3fa8b99..e8f3745 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -34,7 +34,6 @@ import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; -import android.util.Log; import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.View; @@ -126,25 +125,16 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { updateNotification(); } - private void cancelOther(int showing) { - if (mShowing != SHOWING_NOTHING && mShowing != showing) { - mNoMan.cancel(TAG_NOTIFICATION, ID_NOTIFICATION); // workaround no HUN on updates - } - } - private void updateNotification() { Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger); if (mInvalidCharger) { - cancelOther(SHOWING_INVALID_CHARGER); showInvalidChargerNotification(); mShowing = SHOWING_INVALID_CHARGER; } else if (mWarning) { - cancelOther(SHOWING_WARNING); showWarningNotification(); mShowing = SHOWING_WARNING; } else if (mSaver) { - cancelOther(SHOWING_SAVER); showSaverNotification(); mShowing = SHOWING_SAVER; } else { @@ -312,6 +302,12 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { @Override public void dismissInvalidChargerWarning() { + dismissInvalidChargerNotification(); + mFallbackDialogs.dismissInvalidChargerWarning(); + } + + private void dismissInvalidChargerNotification() { + Slog.i(TAG, "dismissing invalid charger notification"); mInvalidCharger = false; updateNotification(); } @@ -355,10 +351,12 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - Log.d(TAG, "got " + action); + Slog.i(TAG, "Received " + action); if (action.equals(ACTION_SHOW_FALLBACK_WARNING)) { + dismissLowBatteryNotification(); mFallbackDialogs.showLowBatteryWarning(false /*playSound*/); } else if (action.equals(ACTION_SHOW_FALLBACK_CHARGER)) { + dismissInvalidChargerNotification(); mFallbackDialogs.showInvalidChargerWarning(); } else if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) { dismissLowBatteryNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 506f9dc..20684a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -133,7 +133,6 @@ public abstract class BaseStatusBar extends SystemUI implements // for heads up notifications protected HeadsUpNotificationView mHeadsUpNotificationView; protected int mHeadsUpNotificationDecay; - protected long mInterruptingNotificationTime; // used to notify status bar for suppressing notification LED protected boolean mPanelSlightlyVisible; @@ -1420,6 +1419,9 @@ public abstract class BaseStatusBar extends SystemUI implements && !TextUtils.equals(notification.getNotification().tickerText, oldEntry.notification.getNotification().tickerText); + final boolean shouldInterrupt = shouldInterrupt(notification); + final boolean alertAgain = alertAgain(oldEntry); + boolean updateSuccessful = false; if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged) { if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); @@ -1439,8 +1441,6 @@ public abstract class BaseStatusBar extends SystemUI implements } } - final boolean shouldInterrupt = shouldInterrupt(notification); - final boolean alertAgain = alertAgain(oldEntry); if (wasHeadsUp) { if (shouldInterrupt) { updateHeadsUpViews(oldEntry, notification); @@ -1462,24 +1462,52 @@ public abstract class BaseStatusBar extends SystemUI implements } mNotificationData.updateRanking(ranking); updateNotifications(); + updateSuccessful = true; } catch (RuntimeException e) { // It failed to add cleanly. Log, and remove the view from the panel. Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); - removeNotificationViews(key, ranking); - addNotificationViews(notification, ranking); } - } else { + } + if (!updateSuccessful) { if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key); - if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed")); - removeNotificationViews(key, ranking); - addNotificationViews(notification, ranking); - final NotificationData.Entry newEntry = mNotificationData.findByKey(key); - final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); - if (userChangedExpansion) { - boolean userExpanded = oldEntry.row.isUserExpanded(); - newEntry.row.setUserExpanded(userExpanded); - newEntry.row.notifyHeightChanged(); + if (wasHeadsUp) { + if (shouldInterrupt) { + if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key); + Entry newEntry = new Entry(notification, null); + ViewGroup holder = mHeadsUpNotificationView.getHolder(); + if (inflateViewsForHeadsUp(newEntry, holder)) { + mHeadsUpNotificationView.showNotification(newEntry); + if (alertAgain) { + resetHeadsUpDecayTimer(); + } + } else { + Log.w(TAG, "Couldn't create new updated headsup for package " + + contentView.getPackage()); + } + } else { + if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key); + oldEntry.notification = notification; + mHeadsUpNotificationView.releaseAndClose(); + return; + } + } else { + if (shouldInterrupt && alertAgain) { + if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key); + removeNotificationViews(key, ranking); + addNotificationInternal(notification, ranking); //this will pop the headsup + } else { + if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key); + removeNotificationViews(key, ranking); + addNotificationViews(notification, ranking); + final NotificationData.Entry newEntry = mNotificationData.findByKey(key); + final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); + if (userChangedExpansion) { + boolean userExpanded = oldEntry.row.isUserExpanded(); + newEntry.row.setUserExpanded(userExpanded); + newEntry.row.notifyHeightChanged(); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index a2dbf75..b23992d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1068,8 +1068,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Entry interruptionCandidate = new Entry(notification, null); ViewGroup holder = mHeadsUpNotificationView.getHolder(); if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { - mInterruptingNotificationTime = System.currentTimeMillis(); - // 1. Populate mHeadsUpNotificationView mHeadsUpNotificationView.showNotification(interruptionCandidate); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index 0f6c4b2..0a48e34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -78,8 +78,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. } public boolean showNotification(NotificationData.Entry headsUp) { - // bump any previous heads up back to the shade - release(); + if (mHeadsUp != null && headsUp != null && !mHeadsUp.key.equals(headsUp.key)) { + // bump any previous heads up back to the shade + release(); + } mHeadsUp = headsUp; if (mContentHolder != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index d6ff4fc..cbad9dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -186,7 +186,7 @@ public class StackScrollAlgorithm { if (!child.isTransparent()) { // Only update the previous values if we are not transparent, // otherwise we would clip to a transparent view. - previousNotificationStart = newYTranslation + child.getClipTopAmount(); + previousNotificationStart = newYTranslation + state.clipTopAmount; previousNotificationEnd = newNotificationEnd; previousNotificationIsSwiped = child.getTranslationX() != 0; } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index cacf66b..a86f466 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -435,6 +435,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { * be done once per window. */ private WindowState mWinDismissingKeyguard; + /** The window that is currently showing "over" the keyguard. If there is an app window + * belonging to another app on top of this the keyguard shows. If there is a fullscreen + * app window under this, still dismiss the keyguard but don't show the app underneath. Show + * the wallpaper. */ + private WindowState mWinShowWhenLocked; + boolean mShowingLockscreen; boolean mShowingDream; boolean mDreamingLockscreen; @@ -1669,8 +1675,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean doesForceHide(WindowManager.LayoutParams attrs) { - return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; + public boolean isForceHiding(WindowManager.LayoutParams attrs) { + return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || + (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden()); } @Override @@ -3119,10 +3126,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, - WindowState attached) { + public void layoutWindowLw(WindowState win, WindowState attached) { // we've already done the status bar - if ((win == mStatusBar && !doesForceHide(attrs)) || win == mNavigationBar) { + final WindowManager.LayoutParams attrs = win.getAttrs(); + if ((win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) == 0) || + win == mNavigationBar) { return; } final boolean isDefaultDisplay = win.isDefaultDisplay(); @@ -3603,12 +3611,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDismissKeyguard = DISMISS_KEYGUARD_NONE; mShowingLockscreen = false; mShowingDream = false; + mWinShowWhenLocked = null; } /** {@inheritDoc} */ @Override - public void applyPostLayoutPolicyLw(WindowState win, - WindowManager.LayoutParams attrs) { + public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" + win.isVisibleOrBehindKeyguardLw()); final int fl = PolicyControl.getWindowFlags(win, attrs); @@ -3646,9 +3654,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0; final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0; + final boolean secureKeyguard = isKeyguardSecure(); if (appWindow) { - if (showWhenLocked || (dismissKeyguard && !isKeyguardSecure())) { + if (showWhenLocked || (dismissKeyguard && !secureKeyguard)) { + // Remove any previous windows with the same appToken. mAppsToBeHidden.remove(win.getAppToken()); + if (mAppsToBeHidden.isEmpty() && showWhenLocked && + isKeyguardSecureIncludingHidden()) { + mWinShowWhenLocked = win; + mHideLockScreen = true; + mForceStatusBarFromKeyguard = false; + } } else { mAppsToBeHidden.add(win.getAppToken()); } @@ -3670,13 +3686,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDismissKeyguard = mWinDismissingKeyguard == win ? DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; mWinDismissingKeyguard = win; - mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); + mForceStatusBarFromKeyguard = mShowingLockscreen && secureKeyguard; } } if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { mAllowLockscreenWhenOn = true; } } + + if (mWinShowWhenLocked != null && + mWinShowWhenLocked.getAppToken() != win.getAppToken()) { + win.hideLw(false); + } } } } @@ -3684,6 +3705,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int finishPostLayoutPolicyLw() { + if (mWinShowWhenLocked != null && + mWinShowWhenLocked != mTopFullscreenOpaqueWindowState) { + // A dialog is dismissing the keyguard. Put the wallpaper behind it and hide the + // fullscreen window. + // TODO: Make sure FLAG_SHOW_WALLPAPER is restored when dialog is dismissed. Or not. + mWinShowWhenLocked.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + mTopFullscreenOpaqueWindowState.hideLw(false); + mTopFullscreenOpaqueWindowState = mWinShowWhenLocked; + } + int changes = 0; boolean topIsFullscreen = false; @@ -4639,6 +4670,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mKeyguardDelegate.isSecure(); } + // Returns true if keyguard is currently locked whether or not it is currently hidden. + private boolean isKeyguardSecureIncludingHidden() { + return mKeyguardDelegate.isSecure() && mKeyguardDelegate.isShowing(); + } + /** {@inheritDoc} */ public boolean inKeyguardRestrictedKeyInputMode() { if (mKeyguardDelegate == null) return false; diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index 0e57232..c9bba69 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -233,6 +233,9 @@ public class FieldPacker { if (obj != null) { if (RenderScript.sPointerSize == 8) { addI64(obj.getID(null)); + addI64(0); + addI64(0); + addI64(0); } else { addI32((int)obj.getID(null)); @@ -240,6 +243,9 @@ public class FieldPacker { } else { if (RenderScript.sPointerSize == 8) { addI64(0); + addI64(0); + addI64(0); + addI64(0); } else { addI32(0); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 657d5ec..00bda06 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; -import static android.net.ConnectivityManager.NetworkCallbackListener; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_DUMMY; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -5410,7 +5409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutSec, IBinder binder, int legacyType) { + Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { enforceConnectivityInternalPermission(); @@ -5418,7 +5417,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { enforceChangePermission(); } - if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) { + if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) { throw new IllegalArgumentException("Bad timeout specified"); } NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( @@ -5428,9 +5427,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkRequestInfo.REQUEST); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); - if (timeoutSec > 0) { + if (timeoutMs > 0) { mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST, - nri), timeoutSec * 1000); + nri), timeoutMs); } return networkRequest; } @@ -5694,7 +5693,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int a2 = 0; switch (notificationType) { case ConnectivityManager.CALLBACK_LOSING: - a1 = 30; // TODO - read this from NetworkMonitor + a1 = 30 * 1000; // TODO - read this from NetworkMonitor // fall through case ConnectivityManager.CALLBACK_PRECHECK: case ConnectivityManager.CALLBACK_AVAILABLE: diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java new file mode 100644 index 0000000..32bcb69 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -0,0 +1,112 @@ +/* + * 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.annotation.Nullable; +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Handles CEC command <Active Source>. + * + * <p>Used by feature actions that need to handle the command in their flow. + */ +final class ActiveSourceHandler { + private static final String TAG = "ActiveSourceHandler"; + + private final HdmiControlService mService; + private final int mSourceAddress; + private final int mSourcePath; + @Nullable private final IHdmiControlCallback mCallback; + + static ActiveSourceHandler create(HdmiControlService service, int sourceAddress, + int sourcePath, IHdmiControlCallback callback) { + if (service == null) { + Slog.e(TAG, "Wrong arguments"); + return null; + } + return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback); + } + + private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath, + IHdmiControlCallback callback) { + mService = service; + mSourceAddress = sourceAddress; + mSourcePath = sourcePath; + mCallback = callback; + } + + /** + * Handles the incoming active source command. + * + * @param deviceLogicalAddress logical address of the device to be the active source + * @param routingPath routing path of the device to be the active source + */ + void process(int deviceLogicalAddress, int routingPath) { + if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) { + invokeCallback(HdmiCec.RESULT_SUCCESS); + return; + } + HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress); + if (device == null) { + // TODO: Start new device action (Device Discovery) sequence 5. + } + + if (!mService.isInPresetInstallationMode()) { + int prevActiveInput = mService.getActiveInput(); + mService.updateActiveDevice(deviceLogicalAddress, routingPath); + if (prevActiveInput != mService.getActiveInput()) { + // TODO: change port input here. + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + } else { + // TV is in a mode that should keep its current source/input from + // being changed for its operation. Reclaim the active source + // or switch the port back to the one used for the current mode. + if (mService.getActiveSource() == mSourceAddress) { + HdmiCecMessage activeSource = + HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath); + mService.sendCecCommand(activeSource); + mService.updateActiveDevice(deviceLogicalAddress, routingPath); + invokeCallback(HdmiCec.RESULT_SUCCESS); + } else { + int activePath = mService.getActivePath(); + mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress, + routingPath, activePath)); + // TODO: Start port select action here + // PortSelectAction action = new PortSelectAction(mService, mSourceAddress, + // activePath, mCallback); + // mService.addActionAndStart(action); + } + } + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Callback failed:" + e); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java new file mode 100644 index 0000000..f170de0 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -0,0 +1,223 @@ +/* + * 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.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Handles an action that selects a logical device as a new active source. + * + * Triggered by {@link HdmiTvClient}, attempts to select the given target device + * for a new active source. It does its best to wake up the target in standby mode + * before issuing the command >Set Stream path<. + */ +final class DeviceSelectAction extends FeatureAction { + private static final String TAG = "DeviceSelect"; + + // Time in milliseconds we wait for the device power status to switch to 'Standby' + private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000; + + // Time in milliseconds we wait for the device power status to turn to 'On'. + private static final int TIMEOUT_POWER_ON_MS = 5 * 1000; + + // Time in milliseconds we wait for <Active Source>. + private static final int TIMEOUT_ACTIVE_SOURCE_MS = 20 * 1000; + + // The number of times we try to wake up the target device before we give up + // and just send <Set Stream Path>. + private static final int LOOP_COUNTER_MAX = 20; + + // State in which we wait for <Report Power Status> to come in response to the command + // <Give Device Power Status> we have sent. + private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; + + // State in which we wait for the device power status to switch to 'Standby'. + // We wait till the status becomes 'Standby' before we send <Set Stream Path> + // to wake up the device again. + private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2; + + // State in which we wait for the device power status to switch to 'on'. We wait + // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>. + private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; + + // State in which we wait for the <Active Source> in response to the command + // <Set Stream Path> we have sent. We wait as much as TIMEOUT_ACTIVE_SOURCE_MS + // before we give up and mark the action as failure. + private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 4; + + private final HdmiCecDeviceInfo mTarget; + private final IHdmiControlCallback mCallback; + private final int mSourcePath; + + private int mPowerStatusCounter = 0; + + /** + * Constructor. + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address of TV initiating this action + * @param sourcePath physical address of TV + * @param target target logical device that will be a new active source + * @param callback callback object + */ + public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath, + HdmiCecDeviceInfo target, IHdmiControlCallback callback) { + super(service, sourceAddress); + mCallback = callback; + mSourcePath = sourcePath; + mTarget = target; + } + + @Override + public boolean start() { + // TODO: Call the logic that display a banner saying the select action got started. + queryDevicePowerStatus(); + return true; + } + + private void queryDevicePowerStatus() { + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mSourceAddress, mTarget.getLogicalAddress())); + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, TIMEOUT_MS); + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + if (cmd.getSource() != mTarget.getLogicalAddress()) { + return false; + } + int opcode = cmd.getOpcode(); + byte[] params = cmd.getParams(); + + switch (mState) { + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS && params.length == 1) { + return handleReportPowerStatus(params[0]); + } + return false; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) { + int activePath = HdmiUtils.twoBytesToInt(params); + ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback) + .process(cmd.getSource(), activePath); + finish(); + return true; + } + return false; + default: + break; + } + return false; + } + + private boolean handleReportPowerStatus(int powerStatus) { + // TODO: Check TV's own status which might have been updated during the action. + // If in 'Standby' or 'Transit to standby', remove the banner + // and stop this action. Otherwise, send <Set Stream Path> + switch (powerStatus) { + case HdmiCec.POWER_STATUS_ON: + sendSetStreamPath(); + return true; + case HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY: + if (mPowerStatusCounter < 4) { + mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY; + addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS); + } else { + sendSetStreamPath(); + } + return true; + case HdmiCec.POWER_STATUS_STANDBY: + if (mPowerStatusCounter == 0) { + turnOnDevice(); + } else { + sendSetStreamPath(); + } + return true; + case HdmiCec.POWER_STATUS_TRANSIENT_TO_ON: + if (mPowerStatusCounter < LOOP_COUNTER_MAX) { + mState = STATE_WAIT_FOR_DEVICE_POWER_ON; + addTimer(mState, TIMEOUT_POWER_ON_MS); + } else { + sendSetStreamPath(); + } + return true; + } + return false; + } + + private void turnOnDevice() { + sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER); + sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION); + mState = STATE_WAIT_FOR_DEVICE_POWER_ON; + addTimer(mState, TIMEOUT_POWER_ON_MS); + } + + private void sendSetStreamPath() { + sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( + mSourceAddress, mTarget.getPhysicalAddress())); + mState = STATE_WAIT_FOR_ACTIVE_SOURCE; + addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS); + } + + private void sendRemoteKeyCommand(int keyCode) { + sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, + mTarget.getLogicalAddress(), keyCode)); + sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, + mTarget.getLogicalAddress())); + } + + @Override + public void handleTimerEvent(int timeoutState) { + if (mState != timeoutState) { + Slog.w(TAG, "Timer in a wrong state. Ignored."); + return; + } + switch (mState) { + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + sendSetStreamPath(); + break; + case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY: + case STATE_WAIT_FOR_DEVICE_POWER_ON: + mPowerStatusCounter++; + queryDevicePowerStatus(); + break; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + // TODO: Remove the banner + // Display banner "Communication failed. Please check your cable or connection" + invokeCallback(HdmiCec.RESULT_TIMEOUT); + finish(); + break; + } + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Callback failed:" + e); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java index 0ba7773..ae272b4 100644 --- a/services/core/java/com/android/server/hdmi/FeatureAction.java +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -46,8 +46,9 @@ abstract class FeatureAction { // Timer handler message used for timeout event protected static final int MSG_TIMEOUT = 100; - // Default timeout for the incoming command to arrive in response to a request - protected static final int TIMEOUT_MS = 1000; + // Default timeout for the incoming command to arrive in response to a request. + // TODO: Consider reading this value from configuration to allow customization. + protected static final int TIMEOUT_MS = 2000; // Default state used in common by all the feature actions. protected static final int STATE_NONE = 0; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index f869424..a0c635d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.HdmiPortInfo; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; @@ -308,6 +309,10 @@ final class HdmiCecController { return mDeviceInfos.get(logicalAddress); } + HdmiPortInfo[] getPortInfos() { + return nativeGetPortInfos(mNativePtr); + } + /** * Return the locally hosted logical device of a given type. * @@ -641,8 +646,8 @@ final class HdmiCecController { private static native int nativeGetPhysicalAddress(long controllerPtr); private static native int nativeGetVersion(long controllerPtr); private static native int nativeGetVendorId(long controllerPtr); + private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr); private static native void nativeSetOption(long controllerPtr, int flag, int value); private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag); private static native boolean nativeIsConnected(long controllerPtr, int port); - } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 8bd81ea..6394fe7 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -16,8 +16,11 @@ package com.android.server.hdmi; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; import android.util.Slog; import java.util.Locale; @@ -55,6 +58,31 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } + /** + * Performs the action 'device select', or 'one touch play' initiated by TV. + * + * @param targetAddress logical address of the device to select + * @param callback callback object to report the result with + */ + void deviceSelect(int targetAddress, IHdmiControlCallback callback) { + HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress); + if (targetDevice == null) { + invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE); + return; + } + mService.removeAction(DeviceSelectAction.class); + mService.addAndStartAction(new DeviceSelectAction(mService, mAddress, + mService.getPhysicalAddress(), targetDevice, callback)); + } + + private static void invokeCallback(IHdmiControlCallback callback, int result) { + try { + callback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Invoking callback failed:" + e); + } + } + @Override protected boolean handleGetMenuLanguage(HdmiCecMessage message) { HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 1fcb32f..8dbfd85 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -270,6 +270,38 @@ public class HdmiCecMessageBuilder { } /** + * Build <Set Stream Path> command. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param streamPath physical address of the device to start streaming + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildSetStreamPath(int src, int streamPath) { + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_STREAM_PATH, + physicalAddressToParam(streamPath)); + } + + /** + * Build <Routing Change> command. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param oldPath physical address of the currently active routing path + * @param newPath physical address of the new active routing path + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) { + byte[] param = new byte[] { + (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), + (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) + }; + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ROUTING_CHANGE, param); + } + + /** * Build <Give Device Power Status> command. * * @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 index 54b5dcb..8f319ea 100644 --- a/services/core/java/com/android/server/hdmi/HdmiConstants.java +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -39,9 +39,15 @@ final class HdmiConstants { // Constants related to UI Command Codes. // Refer to CEC Table 30 in HDMI Spec v1.4b. + static final int UI_COMMAND_POWER = 0x40; static final int UI_COMMAND_MUTE = 0x43; static final int UI_COMMAND_MUTE_FUNCTION = 0x65; static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66; + static final int UI_COMMAND_POWER_ON_FUNCTION = 0x6D; + + // Bit mask used to get the routing path of the top level device. + // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0. + static final int ROUTING_PATH_TOP_MASK = 0xF000; // Flags used for setOption to CEC HAL. /** diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index d41da30..0d7ac8d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -21,6 +21,7 @@ import android.content.Context; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlService; import android.hardware.hdmi.IHdmiHotplugEventListener; @@ -40,6 +41,7 @@ import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -105,9 +107,8 @@ public final class HdmiControlService extends SystemService { // Used to synchronize the access to the service. private final Object mLock = new Object(); - // Type of logical devices hosted in the system. - @GuardedBy("mLock") - private final int[] mLocalDevices; + // Type of logical devices hosted in the system. Stored in the unmodifiable list. + private final List<Integer> mLocalDevices; // List of listeners registered by callers that want to get notified of // hotplug events. @@ -117,6 +118,9 @@ public final class HdmiControlService extends SystemService { private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords = new ArrayList<>(); + // Handler running on service thread. It's used to run a task in service thread. + private final Handler mHandler = new Handler(); + private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache(); @Nullable @@ -125,6 +129,25 @@ public final class HdmiControlService extends SystemService { @Nullable private HdmiMhlController mMhlController; + // HDMI port information. Stored in the unmodifiable list to keep the static information + // from being modified. + private List<HdmiPortInfo> mPortInfo; + + // Logical address of the active source. + @GuardedBy("mLock") + private int mActiveSource; + + // Active routing path. Physical address of the active source but not all the time, such as + // when the new active source does not claim itself to be one. + @GuardedBy("mLock") + private int mActiveRoutingPath; + + // Set to true while the service is in normal mode. While set to false, no input change is + // allowed. Used for situations where input change can confuse users such as channel auto-scan, + // system upgrade, etc., a.k.a. "prohibit mode". + @GuardedBy("mLock") + private boolean mInputChangeEnabled; + @GuardedBy("mLock") // Whether ARC is "enabled" or not. // TODO: it may need to hold lock if it's accessed from others. @@ -134,13 +157,12 @@ public final class HdmiControlService extends SystemService { // 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 final Handler mHandler = new Handler(); - public HdmiControlService(Context context) { super(context); - mLocalDevices = getContext().getResources().getIntArray( - com.android.internal.R.array.config_hdmiCecLogicalDeviceType); + mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray( + com.android.internal.R.array.config_hdmiCecLogicalDeviceType)); + // TODO: Get control flag from persistent storage + mInputChangeEnabled = true; } @Override @@ -158,14 +180,14 @@ public final class HdmiControlService extends SystemService { if (mMhlController == null) { Slog.i(TAG, "Device does not support MHL-control."); } - + mPortInfo = initPortInfo(); 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. } - private void initializeLocalDevices(final int[] deviceTypes) { + private void initializeLocalDevices(final List<Integer> deviceTypes) { // A container for [Logical Address, Local device info]. final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>(); final SparseIntArray finished = new SparseIntArray(); @@ -189,7 +211,7 @@ public final class HdmiControlService extends SystemService { // Once finish address allocation for all devices, notify // it to each device. - if (deviceTypes.length == finished.size()) { + if (deviceTypes.size() == finished.size()) { notifyAddressAllocated(devices); } } @@ -205,6 +227,66 @@ public final class HdmiControlService extends SystemService { } } + // Initialize HDMI port information. Combine the information from CEC and MHL HAL and + // keep them in one place. + private List<HdmiPortInfo> initPortInfo() { + HdmiPortInfo[] cecPortInfo = null; + + // CEC HAL provides majority of the info while MHL does only MHL support flag for + // each port. Return empty array if CEC HAL didn't provide the info. + if (mCecController != null) { + cecPortInfo = mCecController.getPortInfos(); + } + if (cecPortInfo == null) { + return Collections.emptyList(); + } + + HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0]; + if (mMhlController != null) { + // TODO: Implement plumbing logic to get MHL port information. + // mhlPortInfo = mMhlController.getPortInfos(); + } + + // Use the id (port number) to find the matched info between CEC and MHL to combine them + // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found. + ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length); + for (int i = 0; i < cecPortInfo.length; ++i) { + HdmiPortInfo cec = cecPortInfo[i]; + int id = cec.getId(); + boolean mhlInfoFound = false; + for (HdmiPortInfo mhl : mhlPortInfo) { + if (id == mhl.getId()) { + result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(), + cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported())); + mhlInfoFound = true; + break; + } + } + if (!mhlInfoFound) { + result.add(cec); + } + } + + return Collections.unmodifiableList(result); + } + + /** + * Returns HDMI port information for the given port id. + * + * @param portId HDMI port id + * @return {@link HdmiPortInfo} for the given port + */ + HdmiPortInfo getPortInfo(int portId) { + // mPortInfo is an unmodifiable list and the only reference to its inner list. + // No lock is necessary. + for (HdmiPortInfo info : mPortInfo) { + if (portId == info.getId()) { + return info; + } + } + return null; + } + /** * Returns {@link Looper} for IO operation. * @@ -224,6 +306,41 @@ public final class HdmiControlService extends SystemService { return mHandler.getLooper(); } + int getActiveSource() { + synchronized (mLock) { + return mActiveSource; + } + } + + int getActivePath() { + synchronized (mLock) { + return mActiveRoutingPath; + } + } + + /** + * Returns the path (physical address) of the device at the top of the currently active + * routing path. Used to get the corresponding port address of the HDMI input of the TV. + */ + int getActiveInput() { + synchronized (mLock) { + return mActiveRoutingPath & HdmiConstants.ROUTING_PATH_TOP_MASK; + } + } + + void updateActiveDevice(int logicalAddress, int physicalAddress) { + synchronized (mLock) { + mActiveSource = logicalAddress; + mActiveRoutingPath = physicalAddress; + } + } + + void setInputChangeEnabled(boolean enabled) { + synchronized (mLock) { + mInputChangeEnabled = enabled; + } + } + /** * Returns physical address of the device. */ @@ -238,6 +355,11 @@ public final class HdmiControlService extends SystemService { return mCecController.getVendorId(); } + HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { + assertRunOnServiceThread(); + return mCecController.getDeviceInfo(logicalAddress); + } + /** * Returns version of CEC. */ @@ -256,6 +378,24 @@ public final class HdmiControlService extends SystemService { } /** + * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches + * the given routing path. CEC devices use routing path for its physical address to + * describe the hierarchy of the devices in the network. + * + * @param path routing path or physical address + * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null + */ + HdmiCecDeviceInfo getDeviceInfoByPath(int path) { + assertRunOnServiceThread(); + for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) { + if (info.getPhysicalAddress() == path) { + return info; + } + } + return null; + } + + /** * Add and start a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add and start @@ -304,7 +444,7 @@ public final class HdmiControlService extends SystemService { } // Remove all actions matched with the given Class type. - private <T extends FeatureAction> void removeAction(final Class<T> clazz) { + <T extends FeatureAction> void removeAction(final Class<T> clazz) { removeActionExcept(clazz, null); } @@ -569,11 +709,33 @@ public final class HdmiControlService extends SystemService { @Override public int[] getSupportedTypes() { enforceAccessPermission(); - synchronized (mLock) { - return mLocalDevices; + // mLocalDevices is an unmodifiable list - no lock necesary. + int[] localDevices = new int[mLocalDevices.size()]; + for (int i = 0; i < localDevices.length; ++i) { + localDevices[i] = mLocalDevices.get(i); } + return localDevices; + } + + @Override + public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiCecLocalDeviceTv tv = + (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV); + if (tv == null) { + Slog.w(TAG, "Local playback device not available"); + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + tv.deviceSelect(logicalAddress, callback); + } + }); } + @Override public void oneTouchPlay(final IHdmiControlCallback callback) { enforceAccessPermission(); @@ -710,8 +872,9 @@ public final class HdmiControlService extends SystemService { } boolean isInPresetInstallationMode() { - // TODO: Implement this. - return false; + synchronized (mLock) { + return !mInputChangeEnabled; + } } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index ca09fe6..b534377 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -20,6 +20,10 @@ import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Various utilities to handle HDMI CEC messages. */ @@ -73,6 +77,22 @@ final class HdmiUtils { } /** + * Convert integer array to list of {@link Integer}. + * + * <p>The result is immutable. + * + * @param is integer array + * @return {@link List} instance containing the elements in the given array + */ + static List<Integer> asImmutableList(final int[] is) { + ArrayList<Integer> list = new ArrayList<>(is.length); + for (int type : is) { + list.add(type); + } + return Collections.unmodifiableList(list); + } + + /** * Assemble two bytes into single integer value. * * @param data to be assembled diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java new file mode 100644 index 0000000..26048d2 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -0,0 +1,244 @@ +/* + * 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 java.util.concurrent.TimeUnit; + +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.hdmi.HdmiControlService.SendMessageCallback; + +/** + * Feature action for routing control. Exchanges routing-related commands with other devices + * to determine the new active source. + * + * <p>This action is initiated by various cases: + * <ul> + * <li> Manual TV input switching + * <li> Routing change of a CEC switch other than TV + * <li> New CEC device at the tail of the active routing path + * <li> Removed CEC device from the active routing path + * <li> Routing at CEC enable time + * </ul> + */ +public class RoutingControlAction extends FeatureAction { + private static final String TAG = "RoutingControlAction"; + + // State in which we wait for <Routing Information> to arrive. If timed out, we use the + // latest routing path to set the new active source. + private final static int STATE_WAIT_FOR_ROUTING_INFORMATION = 1; + + // State in which we wait for <Report Power Status> in response to <Give Device Power Status> + // we have sent. If the response tells us the device power is on, we send <Set Stream Path> + // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly + // just show the blank screen. + private final static int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2; + + // Time out in millseconds used for <Routing Information> + private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000; + + // Time out in milliseconds used for <Report Power Status> + private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000; + + private final IHdmiControlCallback mCallback; + + // The latest routing path. Updated by each <Routing Information> from CEC switches. + private int mCurrentRoutingPath; + + RoutingControlAction(HdmiControlService service, int sourceAddress, int portId, + IHdmiControlCallback callback) { + super(service, sourceAddress); + mCallback = callback; + mCurrentRoutingPath = portToPath(portId); + } + + @Override + public boolean start() { + mState = STATE_WAIT_FOR_ROUTING_INFORMATION; + addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS); + return true; + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + int opcode = cmd.getOpcode(); + byte[] params = cmd.getParams(); + if (mState == STATE_WAIT_FOR_ROUTING_INFORMATION + && opcode == HdmiCec.MESSAGE_ROUTING_INFORMATION) { + // Keep updating the physicalAddress as we receive <Routing Information>. + // If the routing path doesn't belong to the currently active one, we should + // ignore it since it might have come from other, routing change sequence. + int routingPath = HdmiUtils.twoBytesToInt(params); + if (isInActiveRoutingPath(mCurrentRoutingPath, routingPath)) { + return true; + } + mCurrentRoutingPath = routingPath; + // Stop possible previous routing change sequence if in progress. + mService.removeAction(RoutingControlAction.class); + addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS); + return true; + } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS + && opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) { + handleReportPowerStatus(cmd.getParams()[0]); + return true; + } + return false; + } + + private void handleReportPowerStatus(int devicePowerStatus) { + int tvPowerStatus = getTvPowerStatus(); + if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) { + if (isPowerStatusOnOrTransientToOn(devicePowerStatus)) { + sendSetStreamPath(); + } else { + // The whole action should be stopped here if the device is in standby mode. + // We don't attempt to wake it up by sending <Set Stream Path>. + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + finish(); + } else { + // TV is going into standby mode. + // TODO: Figure out what to do. + } + } + + private int getTvPowerStatus() { + // TODO: Obtain TV power status. + return HdmiCec.POWER_STATUS_ON; + } + + private static boolean isPowerStatusOnOrTransientToOn(int status) { + return status == HdmiCec.POWER_STATUS_ON || status == HdmiCec.POWER_STATUS_TRANSIENT_TO_ON; + } + + private void sendSetStreamPath() { + sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath)); + } + + private static boolean isInActiveRoutingPath(int activePath, int newPath) { + // Check each nibble of the currently active path and the new path till the position + // where the active nibble is not zero. For (activePath, newPath), + // (1.1.0.0, 1.0.0.0) -> true, new path is a parent + // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant + // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling + // (1.0.0.0, 2.0.0.0) -> false, in a completely different path + for (int i = 12; i >= 0; i -= 4) { + int nibbleActive = (activePath >> i) & 0xF; + if (nibbleActive == 0) { + break; + } + int nibbleNew = (newPath >> i) & 0xF; + if (nibbleNew == 0) { + break; + } + if (nibbleActive != nibbleNew) { + return false; + } + } + return true; + } + + @Override + public void handleTimerEvent(int timeoutState) { + if (mState != timeoutState || mState == STATE_NONE) { + Slog.w("CEC", "Timer in a wrong state. Ignored."); + return; + } + switch (timeoutState) { + case STATE_WAIT_FOR_ROUTING_INFORMATION: + HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath); + if (device == null) { + maybeChangeActiveInput(pathToPort(mCurrentRoutingPath)); + } else { + // TODO: Also check followings and then proceed: + // if routing change was neither triggered by TV at CEC enable time, nor + // at the detection of new device at the end of the active routing path, nor + // by TV power on with HDMI input as the active signal source. + int deviceLogicalAddress = device.getLogicalAddress(); + queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + handlDevicePowerStatusAckResult(error == HdmiCec.RESULT_SUCCESS); + } + }); + } + return; + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + int tvPowerStatus = getTvPowerStatus(); + if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) { + if (!maybeChangeActiveInput(pathToPort(mCurrentRoutingPath))) { + sendSetStreamPath(); + } + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + finish(); + return; + } + } + + // Called whenever an HDMI input of the TV shall become the active input. + private boolean maybeChangeActiveInput(int inputPortPath) { + if (mService.getActiveInput() == inputPortPath) { + return false; + } + // TODO: Remember the currently active input + // if PAP/PIP is active, move the focus to the right window, otherwise switch + // the port. + // Show the OSD input change banner. + return true; + } + + private void queryDevicePowerStatus(int address, SendMessageCallback callback) { + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address), + callback); + } + + private void handlDevicePowerStatusAckResult(boolean acked) { + if (acked) { + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS); + } else { + maybeChangeActiveInput(pathToPort(mCurrentRoutingPath)); + } + } + + // Get the address of the TV port to which the given path is connected. + private static int pathToPort(int path) { + return path & HdmiConstants.ROUTING_PATH_TOP_MASK; + } + + // Given the HDMI port id, return the port address. + private int portToPath(int portId) { + return mService.getPortInfo(portId).getAddress(); + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + // Do nothing. + } + } +} diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index 92b643c..534faba3 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -481,11 +481,10 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne */ private void closeAndCleanupJobH(boolean reschedule) { removeMessages(MSG_TIMEOUT); + mCompletedListener.onJobCompleted(mRunningJob, reschedule); synchronized (mLock) { mWakeLock.release(); mContext.unbindService(JobServiceContext.this); - mCompletedListener.onJobCompleted(mRunningJob, reschedule); - mWakeLock = null; mRunningJob = null; mParams = null; diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 5f22b34..4ac26c1 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -262,16 +262,23 @@ public class JobStore { @Override public void run() { final long startElapsed = SystemClock.elapsedRealtime(); + List<JobStatus> mStoreCopy = new ArrayList<JobStatus>(); synchronized (JobStore.this) { - writeJobsMapImpl(); + // Copy over the jobs so we can release the lock before writing. + for (JobStatus jobStatus : mJobSet) { + JobStatus copy = new JobStatus(jobStatus.getJob(), jobStatus.getUid(), + jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed()); + mStoreCopy.add(copy); + } } + writeJobsMapImpl(mStoreCopy); if (JobSchedulerService.DEBUG) { Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime() - startElapsed) + "ms"); } } - private void writeJobsMapImpl() { + private void writeJobsMapImpl(List<JobStatus> jobList) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XmlSerializer out = new FastXmlSerializer(); @@ -281,8 +288,7 @@ public class JobStore { out.startTag(null, "job-info"); out.attribute(null, "version", Integer.toString(JOBS_FILE_VERSION)); - for (int i = 0; i < mJobSet.size(); i++) { - final JobStatus jobStatus = mJobSet.valueAt(i); + for (JobStatus jobStatus : jobList) { if (DEBUG) { Slog.d(TAG, "Saving job " + jobStatus.getJobId()); } diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index 7e79ff7..daba0d9 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -197,4 +197,4 @@ public class ConnectivityController extends StateController implements + ", UM=" + js.hasUnmeteredConstraint()); } } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 008d2fc..45326f7 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED; @@ -227,7 +228,7 @@ public class WindowAnimator { continue; } final WindowStateAnimator winAnimator = win.mWinAnimator; - if (mPolicy.doesForceHide(win.mAttrs)) { + if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { if (!winAnimator.mAnimating) { // Create a new animation to delay until keyguard is gone on its own. winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f); @@ -268,7 +269,7 @@ public class WindowAnimator { } } - if (mPolicy.doesForceHide(win.mAttrs)) { + if (mPolicy.isForceHiding(win.mAttrs)) { if (!wasAnimating && nowAnimating) { if (WindowManagerService.DEBUG_ANIM || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 76085fa..4eea9c3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8442,7 +8442,7 @@ public class WindowManagerService extends IWindowManager.Stub } win.mLayoutNeeded = false; win.prelayout(); - mPolicy.layoutWindowLw(win, win.mAttrs, null); + mPolicy.layoutWindowLw(win, null); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + " mContainingFrame=" @@ -8494,7 +8494,7 @@ public class WindowManagerService extends IWindowManager.Stub } win.mLayoutNeeded = false; win.prelayout(); - mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + mPolicy.layoutWindowLw(win, win.mAttachedWindow); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + " mContainingFrame=" diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index cbc853d..df3b6d3 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -57,6 +57,8 @@ public: int getVersion(); // Get vendor id used for vendor command. uint32_t getVendorId(); + // Get Port information on all the HDMI ports. + jobjectArray getPortInfos(); // Set a flag and its value. void setOption(int flag, int value); // Set audio return channel status. @@ -69,6 +71,7 @@ public: } private: + static const int INVALID_PHYSICAL_ADDRESS = 0xFFFF; static void onReceived(const hdmi_event_t* event, void* arg); hdmi_cec_device_t* mDevice; @@ -209,11 +212,11 @@ void HdmiCecController::clearLogicaladdress() { } int HdmiCecController::getPhysicalAddress() { - uint16_t physicalAddress = 0xFFFF; - if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) { - return physicalAddress; + uint16_t addr; + if (!mDevice->get_physical_address(mDevice, &addr)) { + return addr; } - return -1; + return INVALID_PHYSICAL_ADDRESS; } int HdmiCecController::getVersion() { @@ -228,6 +231,34 @@ uint32_t HdmiCecController::getVendorId() { return vendorId; } +jobjectArray HdmiCecController::getPortInfos() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jclass hdmiPortInfo = env->FindClass("com/android/server/hdmi/HdmiPortInfo"); + if (hdmiPortInfo == NULL) { + return NULL; + } + jmethodID ctor = env->GetMethodID(hdmiPortInfo, "<init>", "(IIIZZZ)V"); + if (ctor == NULL) { + return NULL; + } + hdmi_port_info* ports; + int numPorts; + mDevice->get_port_info(mDevice, &ports, &numPorts); + jobjectArray res = env->NewObjectArray(numPorts, hdmiPortInfo, NULL); + + // MHL support field will be obtained from MHL HAL. Leave it to false. + jboolean mhlSupported = (jboolean) 0; + for (int i = 0; i < numPorts; ++i) { + hdmi_port_info* info = &ports[i]; + jboolean cecSupported = (jboolean) info->cec_supported; + jboolean arcSupported = (jboolean) info->arc_supported; + jobject infoObj = env->NewObject(hdmiPortInfo, ctor, info->port_num, info->type, + info->physical_address, cecSupported, mhlSupported, arcSupported); + env->SetObjectArrayElement(res, i, infoObj); + } + return res; +} + void HdmiCecController::setOption(int flag, int value) { mDevice->set_option(mDevice, flag, value); } @@ -242,7 +273,6 @@ bool HdmiCecController::isConnected(int port) { return mDevice->is_connected(mDevice, port) == HDMI_CONNECTED; } - // static void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { HdmiCecController* controller = static_cast<HdmiCecController*>(arg); @@ -311,58 +341,50 @@ static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, return controller->sendMessage(message); } -static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr, jint logicalAddress) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); - return controller->addLogicalAddress( - static_cast<cec_logical_address_t>(logicalAddress)); +static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr, + jint logicalAddress) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->addLogicalAddress(static_cast<cec_logical_address_t>(logicalAddress)); } -static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); controller->clearLogicaladdress(); } -static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getPhysicalAddress(); } -static jint nativeGetVersion(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static jint nativeGetVersion(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getVersion(); } static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getVendorId(); } -static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, - jint value) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static jobjectArray nativeGetPortInfos(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->getPortInfos(); +} + +static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, jint value) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); controller->setOption(flag, value); } static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr, jboolean enabled) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); controller->setAudioReturnChannel(enabled == JNI_TRUE); } static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ; } @@ -377,6 +399,9 @@ static JNINativeMethod sMethods[] = { { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, + { "nativeGetPortInfos", + "(J)[Lcom/android/server/hdmi/HdmiPortInfo;", + (void *) nativeGetPortInfos }, { "nativeSetOption", "(JII)V", (void *) nativeSetOption }, { "nativeSetAudioReturnChannel", "(JZ)V", (void *) nativeSetAudioReturnChannel }, { "nativeIsConnected", "(JI)Z", (void *) nativeIsConnected }, @@ -385,8 +410,7 @@ static JNINativeMethod sMethods[] = { #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, - NELEM(sMethods)); + int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); return 0; } diff --git a/telecomm/java/android/telecomm/CallCapabilities.java b/telecomm/java/android/telecomm/CallCapabilities.java index b2b33a3..5aff19c 100644 --- a/telecomm/java/android/telecomm/CallCapabilities.java +++ b/telecomm/java/android/telecomm/CallCapabilities.java @@ -17,7 +17,7 @@ package android.telecomm; /** Defines actions a call currently supports. */ -public class CallCapabilities { +public final class CallCapabilities { /** Call can currently be put on hold or unheld. */ public static final int HOLD = 0x00000001; @@ -27,24 +27,60 @@ public class CallCapabilities { /** Call can currently be merged. */ public static final int MERGE_CALLS = 0x00000004; - /* Call can currently be swapped with another call. */ + /** Call can currently be swapped with another call. */ public static final int SWAP_CALLS = 0x00000008; - /* Call currently supports adding another call to this one. */ + /** Call currently supports adding another call to this one. */ public static final int ADD_CALL = 0x00000010; - /* Call supports responding via text option. */ + /** Call supports responding via text option. */ public static final int RESPOND_VIA_TEXT = 0x00000020; - /* Call can be muted. */ + /** Call can be muted. */ public static final int MUTE = 0x00000040; - /* Call supports generic conference mode. */ + /** Call supports generic conference mode. */ public static final int GENERIC_CONFERENCE = 0x00000080; - /* Call currently supports switch between connections. */ + /** Call currently supports switch between connections. */ public static final int CONNECTION_HANDOFF = 0x00000100; public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; + + public static String toString(int capabilities) { + StringBuilder builder = new StringBuilder(); + builder.append("[Capabilities:"); + if ((capabilities & HOLD) != 0) { + builder.append(" HOLD"); + } + if ((capabilities & SUPPORT_HOLD) != 0) { + builder.append(" SUPPORT_HOLD"); + } + if ((capabilities & MERGE_CALLS) != 0) { + builder.append(" MERGE_CALLS"); + } + if ((capabilities & SWAP_CALLS) != 0) { + builder.append(" SWAP_CALLS"); + } + if ((capabilities & ADD_CALL) != 0) { + builder.append(" ADD_CALL"); + } + if ((capabilities & RESPOND_VIA_TEXT) != 0) { + builder.append(" RESPOND_VIA_TEXT"); + } + if ((capabilities & MUTE) != 0) { + builder.append(" MUTE"); + } + if ((capabilities & GENERIC_CONFERENCE) != 0) { + builder.append(" GENERIC_CONFERENCE"); + } + if ((capabilities & CONNECTION_HANDOFF) != 0) { + builder.append(" HANDOFF"); + } + builder.append("]"); + return builder.toString(); + } + + private CallCapabilities() {} } diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java index a254459..0b5981c 100644 --- a/telecomm/java/android/telecomm/CallService.java +++ b/telecomm/java/android/telecomm/CallService.java @@ -61,7 +61,7 @@ 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_CONFERENCE = 14; private static final int MSG_SPLIT_FROM_CONFERENCE = 15; private static final int MSG_ON_POST_DIAL_CONTINUE = 16; @@ -128,24 +128,12 @@ 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: { + case MSG_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { String conferenceCallId = (String) args.arg1; String callId = (String) args.arg2; - splitFromConference(conferenceCallId, callId); + conference(conferenceCallId, callId); } finally { args.recycle(); } @@ -162,6 +150,9 @@ public abstract class CallService extends Service { } break; } + case MSG_SPLIT_FROM_CONFERENCE: + splitFromConference((String) msg.obj); + break; default: break; } @@ -245,19 +236,16 @@ public abstract class CallService extends Service { } @Override - public void addToConference(String conferenceCallId, List<String> callsToConference) { + public void conference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; - args.arg2 = callsToConference; - mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget(); + args.arg2 = callId; + mMessageHandler.obtainMessage(MSG_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(); + public void splitFromConference(String callId) { + mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget(); } @Override @@ -424,24 +412,22 @@ public abstract class CallService extends Service { public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); /** - * Adds the specified calls to the specified conference call. + * Conferences the specified 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. + * @param callId The call to conference. * @hide */ - public abstract void addToConference(String conferenceCallId, List<String> callIds); + public abstract void conference(String conferenceCallId, String callId); /** - * 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. + * Removes the specified call from a 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); + public abstract void splitFromConference(String callId); public void onPostDialContinue(String callId, boolean proceed) {} public void onPostDialWait(Connection conn, String remaining) {} diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java index 8c3ddad..0ba8161 100644 --- a/telecomm/java/android/telecomm/CallServiceAdapter.java +++ b/telecomm/java/android/telecomm/CallServiceAdapter.java @@ -179,12 +179,12 @@ 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. + * @param canConference Specified whether or not the call can be conferenced. * @hide */ - public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) { + public void setCanConference(String callId, boolean canConference) { try { - mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds); + mAdapter.setCanConference(callId, canConference); } catch (RemoteException ignored) { } } @@ -193,13 +193,14 @@ public final class CallServiceAdapter { * 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. + * @param conferenceCallId The unique ID of the conference call. Null if call is not + * conferenced. * @hide */ - public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) { + public void setIsConferenced(String callId, String conferenceCallId) { try { - mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced); + mAdapter.setIsConferenced(callId, conferenceCallId); } catch (RemoteException ignored) { } } @@ -236,4 +237,16 @@ public final class CallServiceAdapter { } catch (RemoteException e) { } } + + /** + * Indicates that a new conference call has been created. + * + * @param callId The unique ID of the conference call. + */ + public void addConferenceCall(String callId) { + try { + mAdapter.addConferenceCall(callId, null); + } catch (RemoteException ignored) { + } + } } diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java index 344814f..7aee770 100644 --- a/telecomm/java/android/telecomm/Connection.java +++ b/telecomm/java/android/telecomm/Connection.java @@ -19,7 +19,10 @@ package android.telecomm; import android.net.Uri; import android.os.Bundle; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -35,6 +38,8 @@ public abstract class Connection { void onDisconnected(Connection c, int cause, String message); void onRequestingRingback(Connection c, boolean ringback); void onDestroyed(Connection c); + void onConferenceCapableChanged(Connection c, boolean isConferenceCapable); + void onParentConnectionChanged(Connection c, Connection parent); } public static class ListenerBase implements Listener { @@ -65,6 +70,14 @@ public abstract class Connection { /** {@inheritDoc} */ @Override public void onRequestingRingback(Connection c, boolean ringback) {} + + /** ${inheritDoc} */ + @Override + public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {} + + /** ${inheritDoc} */ + @Override + public void onParentConnectionChanged(Connection c, Connection parent) {} } public final class State { @@ -79,10 +92,14 @@ public abstract class Connection { } private final Set<Listener> mListeners = new HashSet<>(); + private final List<Connection> mChildConnections = new ArrayList<>(); + private int mState = State.NEW; private CallAudioState mCallAudioState; private Uri mHandle; private boolean mRequestingRingback = false; + private boolean mIsConferenceCapable = false; + private Connection mParentConnection; /** * Create a new Connection. @@ -176,6 +193,16 @@ public abstract class Connection { } /** + * Separates this Connection from a parent connection. + * + * @hide + */ + public final void separate() { + Log.d(this, "separate"); + onSeparate(); + } + + /** * Abort this Connection. The Connection will immediately transition to * the {@link State#DISCONNECTED} state, and send no notifications of this * or any other future events. @@ -240,6 +267,14 @@ public abstract class Connection { } /** + * TODO(santoscordon): Needs updated documentation. + */ + public final void conference() { + Log.d(this, "conference"); + onConference(); + } + + /** * Inform this Connection that the state of its audio output has been changed externally. * * @param state The new audio state. @@ -274,7 +309,7 @@ public abstract class Connection { } /** - * @return Whether this connection is requesting that the system play a ringback tone + * Returns whether this connection is requesting that the system play a ringback tone * on its behalf. */ public boolean isRequestingRingback() { @@ -282,6 +317,38 @@ public abstract class Connection { } /** + * Returns whether this connection is a conference connection (has child connections). + */ + public boolean isConferenceConnection() { + return !mChildConnections.isEmpty(); + } + + public void setParentConnection(Connection parentConnection) { + Log.d(this, "parenting %s to %s", this, parentConnection); + if (mParentConnection != parentConnection) { + if (mParentConnection != null) { + mParentConnection.removeChild(this); + } + mParentConnection = parentConnection; + if (mParentConnection != null) { + mParentConnection.addChild(this); + // do something if the child connections goes down to ZERO. + } + for (Listener l : mListeners) { + l.onParentConnectionChanged(this, mParentConnection); + } + } + } + + public Connection getParentConnection() { + return mParentConnection; + } + + public List<Connection> getChildConnections() { + return mChildConnections; + } + + /** * Sets the value of the {@link #getHandle()} property and notifies listeners. * * @param handle The new handle. @@ -359,6 +426,32 @@ public abstract class Connection { } /** + * TODO(santoscordon): Needs documentation. + */ + protected void setIsConferenceCapable(boolean isConferenceCapable) { + if (mIsConferenceCapable != isConferenceCapable) { + mIsConferenceCapable = isConferenceCapable; + for (Listener l : mListeners) { + l.onConferenceCapableChanged(this, mIsConferenceCapable); + } + } + } + + /** + * TODO(santoscordon): Needs documentation. + */ + protected void setDestroyed() { + // It is possible that onDestroy() will trigger the listener to remove itself which will + // result in a concurrent modification exception. To counteract this we make a copy of the + // listeners and iterate on that. + for (Listener l : new ArrayList<>(mListeners)) { + if (mListeners.contains(l)) { + l.onDestroyed(this); + } + } + } + + /** * Notifies this Connection and listeners that the {@link #getCallAudioState()} property * has a new value. * @@ -418,6 +511,11 @@ public abstract class Connection { protected void onDisconnect() {} /** + * Notifies this Connection of a request to disconnect. + */ + protected void onSeparate() {} + + /** * Notifies this Connection of a request to abort. */ protected void onAbort() {} @@ -449,6 +547,28 @@ public abstract class Connection { */ protected void onPostDialContinue(boolean proceed) {} + /** + * TODO(santoscordon): Needs documentation. + */ + protected void onConference() {} + + /** + * TODO(santoscordon): Needs documentation. + */ + protected void onChildrenChanged(List<Connection> children) {} + + private void addChild(Connection connection) { + Log.d(this, "adding child %s", connection); + mChildConnections.add(connection); + onChildrenChanged(mChildConnections); + } + + private void removeChild(Connection connection) { + Log.d(this, "removing child %s", connection); + mChildConnections.remove(connection); + onChildrenChanged(mChildConnections); + } + private void setState(int state) { Log.d(this, "setState: %s", stateToString(state)); onSetState(state); diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index 59e977d..9dfad2d 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -20,10 +20,15 @@ import android.net.Uri; import android.os.Bundle; import android.telephony.DisconnectCause; +import android.os.SystemClock; + +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; /** * A {@link android.app.Service} that provides telephone connections to @@ -32,7 +37,6 @@ import java.util.Map; public abstract class ConnectionService extends CallService { // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); - private static final Connection NULL_CONNECTION = new Connection() {}; // Mappings from Connections to IDs as understood by the current CallService implementation @@ -99,6 +103,20 @@ public abstract class ConnectionService extends CallService { Log.d(this, "Adapter onRingback %b", ringback); getAdapter().setRequestingRingback(id, ringback); } + + @Override + public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) { + String id = mIdByConnection.get(c); + getAdapter().setCanConference(id, isConferenceCapable); + } + + /** ${inheritDoc} */ + @Override + public void onParentConnectionChanged(Connection c, Connection parent) { + String id = mIdByConnection.get(c); + String parentId = parent == null ? null : mIdByConnection.get(parent); + getAdapter().setIsConferenced(id, parentId); + } }; @Override @@ -110,8 +128,7 @@ public abstract class ConnectionService extends CallService { @Override public void onResult(Uri handle, Subscription... result) { boolean isCompatible = result.length > 0; - Log.d(this, "adapter setIsCompatibleWith " - + callInfo.getId() + " " + isCompatible); + Log.d(this, "adapter setIsCompatibleWith "); getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible); } @@ -135,7 +152,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { - if (result.length != 1) { + if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo); getAdapter().handleFailedOutgoingCall( request, @@ -145,10 +162,10 @@ public abstract class ConnectionService extends CallService { c.abort(); } } else { - addConnection(callInfo.getId(), result[0]); Log.d(this, "adapter handleSuccessfulOutgoingCall %s", callInfo.getId()); getAdapter().handleSuccessfulOutgoingCall(callInfo.getId()); + addConnection(callInfo.getId(), result[0]); } } @@ -177,7 +194,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { - if (result.length != 1) { + if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callId); getAdapter().handleFailedOutgoingCall( request, @@ -258,27 +275,43 @@ public abstract class ConnectionService extends CallService { /** @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); + public final void conference(final String conferenceCallId, String callId) { + Log.d(this, "conference %s, %s", conferenceCallId, callId); + + Connection connection = findConnectionForAction(callId, "conference"); + if (connection == NULL_CONNECTION) { + Log.w(this, "Connection missing in conference request %s.", callId); + return; } - // TODO(santoscordon): Find an existing conference call or create a new one. Then call - // conferenceWith on it. + onCreateConferenceConnection(conferenceCallId, connection, + new Response<String, Connection>() { + /** ${inheritDoc} */ + @Override + public void onResult(String ignored, Connection... result) { + Log.d(this, "onCreateConference.Response %s", (Object[]) result); + if (result != null && result.length == 1) { + Connection conferenceConnection = result[0]; + if (!mIdByConnection.containsKey(conferenceConnection)) { + Log.v(this, "sending new conference call %s", conferenceCallId); + getAdapter().addConferenceCall(conferenceCallId); + addConnection(conferenceCallId, conferenceConnection); + } + } + } + + /** ${inheritDoc} */ + @Override + public void onError(String request, int code, String reason) { + // no-op + } + }); } /** @hide */ @Override - public final void splitFromConference(String conferenceCallId, String callId) { - Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId); + public final void splitFromConference(String callId) { + Log.d(this, "splitFromConference(%s)", callId); Connection connection = findConnectionForAction(callId, "splitFromConference"); if (connection == NULL_CONNECTION) { @@ -309,6 +342,13 @@ public abstract class ConnectionService extends CallService { } /** + * Returns all connections currently associated with this connection service. + */ + public Collection<Connection> getAllConnections() { + return mConnectionById.values(); + } + + /** * Find a set of Subscriptions matching a given handle (e.g. phone number). * * @param handle A handle (e.g. phone number) with which to connect. @@ -329,6 +369,21 @@ public abstract class ConnectionService extends CallService { Response<ConnectionRequest, Connection> callback) {} /** + * Returns a new or existing conference connection when the the user elects to convert the + * specified connection into a conference call. The specified connection can be any connection + * which had previously specified itself as conference-capable including both simple connections + * and connections previously returned from this method. + * + * @param connection The connection from which the user opted to start a conference call. + * @param token The token to be passed into the response callback. + * @param callback The callback for providing the potentially-new conference connection. + */ + public void onCreateConferenceConnection( + String token, + Connection connection, + Response<String, Connection> callback) {} + + /** * Create a Connection to match an incoming connection notification. * * @param request Data encapsulating details of the desired Connection. @@ -338,6 +393,20 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} + /** + * Notifies that a connection has been added to this connection service and sent to Telecomm. + * + * @param connection The connection which was added. + */ + public void onConnectionAdded(Connection connection) {} + + /** + * Notified that a connection has been removed from this connection service. + * + * @param connection The connection which was removed. + */ + public void onConnectionRemoved(Connection connection) {} + static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { @@ -387,12 +456,14 @@ public abstract class ConnectionService extends CallService { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); + onConnectionAdded(connection); } private void removeConnection(Connection connection) { connection.removeConnectionListener(mConnectionListener); mConnectionById.remove(mIdByConnection.get(connection)); mIdByConnection.remove(connection); + onConnectionRemoved(connection); } private Connection findConnectionForAction(String callId, String action) { diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index 0bef419..86b7a50 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -199,15 +199,14 @@ public final class InCallAdapter { } /** - * Instructs Telecomm to conference the specified calls together. + * Instructs Telecomm to conference the specified call. * * @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) { + public void conference(String callId) { try { - mAdapter.conferenceWith(callId, callIdToConference); + mAdapter.conference(callId); } catch (RemoteException ignored) { } } @@ -219,7 +218,7 @@ public final class InCallAdapter { * @param callId The unique ID of the call. * @hide */ - void splitFromConference(String callId) { + public 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 b531ccd..432e378 100644 --- a/telecomm/java/android/telecomm/InCallCall.java +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -39,7 +39,6 @@ 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; @@ -57,8 +56,7 @@ public final class InCallCall implements Parcelable { CallServiceDescriptor descriptor, CallServiceDescriptor handoffDescriptor) { this(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis, - handle, gatewayInfo, descriptor, handoffDescriptor, Collections.EMPTY_LIST, null, - Collections.EMPTY_LIST); + handle, gatewayInfo, descriptor, handoffDescriptor, null, Collections.EMPTY_LIST); } /** @hide */ @@ -73,7 +71,6 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo, CallServiceDescriptor descriptor, CallServiceDescriptor handoffDescriptor, - List<String> conferenceCapableCallIds, String parentCallId, List<String> childCallIds) { mId = id; @@ -86,7 +83,6 @@ public final class InCallCall implements Parcelable { mGatewayInfo = gatewayInfo; mCurrentCallServiceDescriptor = descriptor; mHandoffCallServiceDescriptor = handoffDescriptor; - mConferenceCapableCallIds = conferenceCapableCallIds; mParentCallId = parentCallId; mChildCallIds = childCallIds; } @@ -151,14 +147,6 @@ public final class InCallCall implements Parcelable { } /** - * 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 */ @@ -191,14 +179,12 @@ 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, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis, handle, gatewayInfo, descriptor, handoffDescriptor, - conferenceCapableCallIds, parentCallId, childCallIds); + parentCallId, childCallIds); } @Override @@ -226,8 +212,12 @@ 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); } + + @Override + public String toString() { + return String.format("[%s, parent:%s, children:%s]", mId, mParentCallId, mChildCallIds); + } } diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl index 9139aa6..827f331 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl @@ -56,9 +56,9 @@ oneway interface ICallService { void stopDtmfTone(String callId); - void addToConference(String conferenceCallId, in List<String> callIds); + void conference(String conferenceCallId, String callId); - void splitFromConference(String conferenceCallId, String callId); + void splitFromConference(String callId); void onPostDialContinue(String callId, boolean proceed); } diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl index b380293..6e176eb 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl @@ -47,9 +47,11 @@ oneway interface ICallServiceAdapter { void setRequestingRingback(String callId, boolean ringing); - void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds); + void setCanConference(String callId, boolean canConference); - void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced); + void setIsConferenced(String callId, String conferenceCallId); + + void addConferenceCall(String callId, in CallInfo callInfo); 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 f144043..5717456 100644 --- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl @@ -48,7 +48,7 @@ oneway interface IInCallAdapter { void handoffCall(String callId); - void conferenceWith(String callId, String callIdToConference); + void conference(String callId); void splitFromConference(String callId); } |
