diff options
14 files changed, 535 insertions, 17 deletions
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 7d65736..0071865 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -325,6 +325,16 @@ public final class DisplayManager { mGlobal.connectWifiDisplay(deviceAddress); } + /** @hide */ + public void pauseWifiDisplay() { + mGlobal.pauseWifiDisplay(); + } + + /** @hide */ + public void resumeWifiDisplay() { + mGlobal.resumeWifiDisplay(); + } + /** * Disconnects from the current Wifi display. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 10c14ff..936a086 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -287,6 +287,22 @@ public final class DisplayManagerGlobal { } } + public void pauseWifiDisplay() { + try { + mDm.pauseWifiDisplay(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to pause Wifi display.", ex); + } + } + + public void resumeWifiDisplay() { + try { + mDm.resumeWifiDisplay(); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to resume Wifi display.", ex); + } + } + public void disconnectWifiDisplay() { try { mDm.disconnectWifiDisplay(); diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index afaf436..6b2c887 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -55,4 +55,10 @@ interface IDisplayManager { // No permissions required but must be same Uid as the creator. void releaseVirtualDisplay(in IBinder token); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void pauseWifiDisplay(); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void resumeWifiDisplay(); } diff --git a/core/java/android/hardware/display/WifiDisplaySessionInfo.java b/core/java/android/hardware/display/WifiDisplaySessionInfo.java new file mode 100644 index 0000000..33d2725 --- /dev/null +++ b/core/java/android/hardware/display/WifiDisplaySessionInfo.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 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.display; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This class contains information regarding a wifi display session + * (such as session id, source ip address, etc.). This is needed for + * Wifi Display Certification process. + * <p> + * This object is immutable. + * </p> + * + * @hide + */ +public final class WifiDisplaySessionInfo implements Parcelable { + private final boolean mClient; + private final int mSessionId; + private final String mGroupId; + private final String mPassphrase; + private final String mIP; + + public static final Creator<WifiDisplaySessionInfo> CREATOR = + new Creator<WifiDisplaySessionInfo>() { + @Override + public WifiDisplaySessionInfo createFromParcel(Parcel in) { + boolean client = (in.readInt() != 0); + int session = in.readInt(); + String group = in.readString(); + String pp = in.readString(); + String ip = in.readString(); + + return new WifiDisplaySessionInfo(client, session, group, pp, ip); + } + + @Override + public WifiDisplaySessionInfo[] newArray(int size) { + return new WifiDisplaySessionInfo[size]; + } + }; + + public WifiDisplaySessionInfo() { + this(true, 0, "", "", ""); + } + + public WifiDisplaySessionInfo( + boolean client, int session, String group, String pp, String ip) { + mClient = client; + mSessionId = session; + mGroupId = group; + mPassphrase = pp; + mIP = ip; + } + + public boolean isClient() { + return mClient; + } + + public int getSessionId() { + return mSessionId; + } + + public String getGroupId() { + return mGroupId; + } + + public String getPassphrase() { + return mPassphrase; + } + + public String getIP() { + return mIP; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mClient ? 1 : 0); + dest.writeInt(mSessionId); + dest.writeString(mGroupId); + dest.writeString(mPassphrase); + dest.writeString(mIP); + } + + @Override + public int describeContents() { + return 0; + } + + // For debugging purposes only. + @Override + public String toString() { + return "WifiDisplaySessionInfo:" + +"\n Client/Owner: " + (mClient ? "Client":"Owner") + +"\n GroupId: " + mGroupId + +"\n Passphrase: " + mPassphrase + +"\n SessionId: " + mSessionId + +"\n IP Address: " + mIP + ; + } +} diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java index 77acdc0..5216727 100644 --- a/core/java/android/hardware/display/WifiDisplayStatus.java +++ b/core/java/android/hardware/display/WifiDisplayStatus.java @@ -39,6 +39,9 @@ public final class WifiDisplayStatus implements Parcelable { private final WifiDisplay mActiveDisplay; private final WifiDisplay[] mDisplays; + /** Session info needed for Miracast Certification */ + private final WifiDisplaySessionInfo mSessionInfo; + /** Feature state: Wifi display is not available on this device. */ public static final int FEATURE_STATE_UNAVAILABLE = 0; /** Feature state: Wifi display is disabled, probably because Wifi is disabled. */ @@ -76,8 +79,11 @@ public final class WifiDisplayStatus implements Parcelable { displays[i] = WifiDisplay.CREATOR.createFromParcel(in); } + WifiDisplaySessionInfo sessionInfo = + WifiDisplaySessionInfo.CREATOR.createFromParcel(in); + return new WifiDisplayStatus(featureState, scanState, activeDisplayState, - activeDisplay, displays); + activeDisplay, displays, sessionInfo); } public WifiDisplayStatus[] newArray(int size) { @@ -87,11 +93,11 @@ public final class WifiDisplayStatus implements Parcelable { public WifiDisplayStatus() { this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED, - null, WifiDisplay.EMPTY_ARRAY); + null, WifiDisplay.EMPTY_ARRAY, null); } - public WifiDisplayStatus(int featureState, int scanState, - int activeDisplayState, WifiDisplay activeDisplay, WifiDisplay[] displays) { + public WifiDisplayStatus(int featureState, int scanState, int activeDisplayState, + WifiDisplay activeDisplay, WifiDisplay[] displays, WifiDisplaySessionInfo sessionInfo) { if (displays == null) { throw new IllegalArgumentException("displays must not be null"); } @@ -101,6 +107,8 @@ public final class WifiDisplayStatus implements Parcelable { mActiveDisplayState = activeDisplayState; mActiveDisplay = activeDisplay; mDisplays = displays; + + mSessionInfo = (sessionInfo != null) ? sessionInfo : new WifiDisplaySessionInfo(); } /** @@ -144,13 +152,20 @@ public final class WifiDisplayStatus implements Parcelable { /** * Gets the list of Wifi displays, returns a combined list of all available - * Wifi displays as reported by the most recent scan, and all remembered + * Wifi displays as reported by the most recent scan, and all remembered * Wifi displays (not necessarily available at the time). */ public WifiDisplay[] getDisplays() { return mDisplays; } + /** + * Gets the Wifi display session info (required for certification only) + */ + public WifiDisplaySessionInfo getSessionInfo() { + return mSessionInfo; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mFeatureState); @@ -168,6 +183,8 @@ public final class WifiDisplayStatus implements Parcelable { for (WifiDisplay display : mDisplays) { display.writeToParcel(dest, flags); } + + mSessionInfo.writeToParcel(dest, flags); } @Override @@ -183,6 +200,7 @@ public final class WifiDisplayStatus implements Parcelable { + ", activeDisplayState=" + mActiveDisplayState + ", activeDisplay=" + mActiveDisplay + ", displays=" + Arrays.toString(mDisplays) + + ", sessionInfo=" + mSessionInfo + "}"; } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b3309e1..0b51b8a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5075,6 +5075,14 @@ public final class Settings { public static final String WIFI_DISPLAY_ON = "wifi_display_on"; /** + * Whether Wifi display certification mode is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String WIFI_DISPLAY_CERTIFICATION_ON = + "wifi_display_certification_on"; + + /** * Whether to notify the user of open networks. * <p> * If not connected and the scan results have an open network, we will diff --git a/core/jni/android_media_RemoteDisplay.cpp b/core/jni/android_media_RemoteDisplay.cpp index 80d13be..3fd8ed9 100644 --- a/core/jni/android_media_RemoteDisplay.cpp +++ b/core/jni/android_media_RemoteDisplay.cpp @@ -61,7 +61,7 @@ protected: public: virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer, - uint32_t width, uint32_t height, uint32_t flags) { + uint32_t width, uint32_t height, uint32_t flags, uint32_t session) { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject surfaceObj = android_view_Surface_createFromIGraphicBufferProducer(env, bufferProducer); @@ -73,7 +73,7 @@ public: env->CallVoidMethod(mRemoteDisplayObjGlobal, gRemoteDisplayClassInfo.notifyDisplayConnected, - surfaceObj, width, height, flags); + surfaceObj, width, height, flags, session); env->DeleteLocalRef(surfaceObj); checkAndClearExceptionFromCallback(env, "notifyDisplayConnected"); } @@ -117,6 +117,14 @@ public: mDisplay->dispose(); } + void pause() { + mDisplay->pause(); + } + + void resume() { + mDisplay->resume(); + } + private: sp<IRemoteDisplay> mDisplay; sp<NativeRemoteDisplayClient> mClient; @@ -149,6 +157,16 @@ static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr return reinterpret_cast<jint>(wrapper); } +static void nativePause(JNIEnv* env, jobject remoteDisplayObj, jint ptr) { + NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr); + wrapper->pause(); +} + +static void nativeResume(JNIEnv* env, jobject remoteDisplayObj, jint ptr) { + NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr); + wrapper->resume(); +} + static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jint ptr) { NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr); delete wrapper; @@ -161,6 +179,10 @@ static JNINativeMethod gMethods[] = { (void*)nativeListen }, {"nativeDispose", "(I)V", (void*)nativeDispose }, + {"nativePause", "(I)V", + (void*)nativePause }, + {"nativeResume", "(I)V", + (void*)nativeResume }, }; int register_android_media_RemoteDisplay(JNIEnv* env) @@ -171,7 +193,7 @@ int register_android_media_RemoteDisplay(JNIEnv* env) jclass clazz = env->FindClass("android/media/RemoteDisplay"); gRemoteDisplayClassInfo.notifyDisplayConnected = env->GetMethodID(clazz, "notifyDisplayConnected", - "(Landroid/view/Surface;III)V"); + "(Landroid/view/Surface;IIII)V"); gRemoteDisplayClassInfo.notifyDisplayDisconnected = env->GetMethodID(clazz, "notifyDisplayDisconnected", "()V"); gRemoteDisplayClassInfo.notifyDisplayError = diff --git a/media/java/android/media/RemoteDisplay.java b/media/java/android/media/RemoteDisplay.java index b463d26..7afce1a 100644 --- a/media/java/android/media/RemoteDisplay.java +++ b/media/java/android/media/RemoteDisplay.java @@ -42,6 +42,8 @@ public final class RemoteDisplay { private native int nativeListen(String iface); private native void nativeDispose(int ptr); + private native void nativePause(int ptr); + private native void nativeResume(int ptr); private RemoteDisplay(Listener listener, Handler handler) { mListener = listener; @@ -87,6 +89,14 @@ public final class RemoteDisplay { dispose(false); } + public void pause() { + nativePause(mPtr); + } + + public void resume() { + nativeResume(mPtr); + } + private void dispose(boolean finalized) { if (mPtr != 0) { if (mGuard != null) { @@ -113,11 +123,11 @@ public final class RemoteDisplay { // Called from native. private void notifyDisplayConnected(final Surface surface, - final int width, final int height, final int flags) { + final int width, final int height, final int flags, final int session) { mHandler.post(new Runnable() { @Override public void run() { - mListener.onDisplayConnected(surface, width, height, flags); + mListener.onDisplayConnected(surface, width, height, flags, session); } }); } @@ -146,7 +156,8 @@ public final class RemoteDisplay { * Listener invoked when the remote display connection changes state. */ public interface Listener { - void onDisplayConnected(Surface surface, int width, int height, int flags); + void onDisplayConnected(Surface surface, + int width, int height, int flags, int session); void onDisplayDisconnected(); void onDisplayError(int error); } diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 659163c..249c8b0 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -497,6 +497,48 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } } + @Override + public void pauseWifiDisplay() { + if (mContext.checkCallingPermission( + android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY" + + "permission to pause a wifi display session."); + } + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestPauseLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void resumeWifiDisplay() { + if (mContext.checkCallingPermission( + android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY" + + "permission to resume a wifi display session."); + } + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestResumeLocked(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override // Binder call public void disconnectWifiDisplay() { final long token = Binder.clearCallingIdentity(); diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index a9da30f..f7bbdf8 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -30,6 +30,7 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplay; +import android.hardware.display.WifiDisplaySessionInfo; import android.hardware.display.WifiDisplayStatus; import android.media.RemoteDisplay; import android.os.Handler; @@ -93,6 +94,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY; private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; + private WifiDisplaySessionInfo mSessionInfo; private boolean mPendingStatusChangeBroadcast; private boolean mPendingNotificationUpdate; @@ -204,6 +206,36 @@ final class WifiDisplayAdapter extends DisplayAdapter { return false; } + public void requestPauseLocked() { + if (DEBUG) { + Slog.d(TAG, "requestPauseLocked"); + } + + getHandler().post(new Runnable() { + @Override + public void run() { + if (mDisplayController != null) { + mDisplayController.requestPause(); + } + } + }); + } + + public void requestResumeLocked() { + if (DEBUG) { + Slog.d(TAG, "requestResumeLocked"); + } + + getHandler().post(new Runnable() { + @Override + public void run() { + if (mDisplayController != null) { + mDisplayController.requestResume(); + } + } + }); + } + public void requestDisconnectLocked() { if (DEBUG) { Slog.d(TAG, "requestDisconnectedLocked"); @@ -267,7 +299,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { if (mCurrentStatus == null) { mCurrentStatus = new WifiDisplayStatus( mFeatureState, mScanState, mActiveDisplayState, - mActiveDisplay, mDisplays); + mActiveDisplay, mDisplays, mSessionInfo); } if (DEBUG) { @@ -580,6 +612,14 @@ final class WifiDisplayAdapter extends DisplayAdapter { } @Override + public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) { + synchronized (getSyncRoot()) { + mSessionInfo = sessionInfo; + scheduleStatusChangedBroadcastLocked(); + } + } + + @Override public void onDisplayChanged(WifiDisplay display) { synchronized (getSyncRoot()) { display = mPersistentDataStore.applyWifiDisplayAlias(display); diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java index 846a74d..cd201f5 100644 --- a/services/java/com/android/server/display/WifiDisplayController.java +++ b/services/java/com/android/server/display/WifiDisplayController.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.display.WifiDisplay; +import android.hardware.display.WifiDisplaySessionInfo; import android.hardware.display.WifiDisplayStatus; import android.media.AudioManager; import android.media.RemoteDisplay; @@ -76,6 +77,7 @@ final class WifiDisplayController implements DumpUtils.Dump { private static final int MAX_THROUGHPUT = 50; private static final int CONNECTION_TIMEOUT_SECONDS = 60; private static final int RTSP_TIMEOUT_SECONDS = 15; + private static final int RTSP_TIMEOUT_SECONDS_CERT_MODE = 120; private static final int DISCOVER_PEERS_MAX_RETRIES = 10; private static final int DISCOVER_PEERS_RETRY_DELAY_MILLIS = 500; @@ -146,6 +148,10 @@ final class WifiDisplayController implements DumpUtils.Dump { private int mAdvertisedDisplayHeight; private int mAdvertisedDisplayFlags; + // Certification + private boolean mWifiDisplayCertMode; + private WifiP2pDevice mThisDevice; + public WifiDisplayController(Context context, Handler handler, Listener listener) { mContext = context; mHandler = handler; @@ -158,6 +164,7 @@ final class WifiDisplayController implements DumpUtils.Dump { intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); context.registerReceiver(mWifiP2pReceiver, intentFilter, null, mHandler); ContentObserver settingsObserver = new ContentObserver(mHandler) { @@ -170,6 +177,8 @@ final class WifiDisplayController implements DumpUtils.Dump { final ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.WIFI_DISPLAY_ON), false, settingsObserver); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, settingsObserver); updateSettings(); } @@ -177,6 +186,8 @@ final class WifiDisplayController implements DumpUtils.Dump { final ContentResolver resolver = mContext.getContentResolver(); mWifiDisplayOnSetting = Settings.Global.getInt(resolver, Settings.Global.WIFI_DISPLAY_ON, 0) != 0; + mWifiDisplayCertMode = Settings.Global.getInt(resolver, + Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0; updateWfdEnableState(); } @@ -223,6 +234,18 @@ final class WifiDisplayController implements DumpUtils.Dump { } } + public void requestPause() { + if (mRemoteDisplay != null) { + mRemoteDisplay.pause(); + } + } + + public void requestResume() { + if (mRemoteDisplay != null) { + mRemoteDisplay.resume(); + } + } + public void requestDisconnect() { disconnect(); } @@ -482,6 +505,7 @@ final class WifiDisplayController implements DumpUtils.Dump { Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName); mDisconnectingDevice = mConnectedDevice; mConnectedDevice = null; + mConnectedDeviceGroupInfo = null; unadvertiseDisplay(); @@ -548,8 +572,12 @@ final class WifiDisplayController implements DumpUtils.Dump { return; // wait for asynchronous callback } - // Step 4. If we wanted to disconnect, then mission accomplished. + // Step 4. If we wanted to disconnect, or we're updating after starting an + // autonomous GO, then mission accomplished. if (mDesiredDevice == null) { + if (mWifiDisplayCertMode) { + mListener.onDisplaySessionInfo(getSessionInfo(mConnectedDeviceGroupInfo, 0)); + } unadvertiseDisplay(); return; // done } @@ -625,13 +653,18 @@ final class WifiDisplayController implements DumpUtils.Dump { mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() { @Override public void onDisplayConnected(Surface surface, - int width, int height, int flags) { + int width, int height, int flags, int session) { if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) { Slog.i(TAG, "Opened RTSP connection with Wifi display: " + mConnectedDevice.deviceName); mRemoteDisplayConnected = true; mHandler.removeCallbacks(mRtspTimeout); + if (mWifiDisplayCertMode) { + mListener.onDisplaySessionInfo( + getSessionInfo(mConnectedDeviceGroupInfo, session)); + } + final WifiDisplay display = createWifiDisplay(mConnectedDevice); advertiseDisplay(display, surface, width, height, flags); } @@ -658,8 +691,29 @@ final class WifiDisplayController implements DumpUtils.Dump { } }, mHandler); - mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000); + // Use extended timeout value for certification, as some tests require user inputs + int rtspTimeout = mWifiDisplayCertMode ? + RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS; + + mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000); + } + } + + private WifiDisplaySessionInfo getSessionInfo(WifiP2pGroup info, int session) { + if (info == null) { + return null; + } + Inet4Address addr = getInterfaceAddress(info); + WifiDisplaySessionInfo sessionInfo = new WifiDisplaySessionInfo( + !info.getOwner().deviceAddress.equals(mThisDevice.deviceAddress), + session, + info.getOwner().deviceAddress + " " + info.getNetworkName(), + info.getPassphrase(), + (addr != null) ? addr.getHostAddress() : ""); + if (DEBUG) { + Slog.d(TAG, sessionInfo.toString()); } + return sessionInfo; } private void handleStateChanged(boolean enabled) { @@ -676,7 +730,7 @@ final class WifiDisplayController implements DumpUtils.Dump { private void handleConnectionChanged(NetworkInfo networkInfo) { mNetworkInfo = networkInfo; if (mWfdEnabled && networkInfo.isConnected()) { - if (mDesiredDevice != null) { + if (mDesiredDevice != null || mWifiDisplayCertMode) { mWifiP2pManager.requestGroupInfo(mWifiP2pChannel, new GroupInfoListener() { @Override public void onGroupInfoAvailable(WifiP2pGroup info) { @@ -698,6 +752,25 @@ final class WifiDisplayController implements DumpUtils.Dump { return; } + if (mWifiDisplayCertMode) { + boolean owner = info.getOwner().deviceAddress + .equals(mThisDevice.deviceAddress); + if (owner && info.getClientList().isEmpty()) { + // this is the case when we started Autonomous GO, + // and no client has connected, save group info + // and updateConnection() + mConnectingDevice = mDesiredDevice = null; + mConnectedDeviceGroupInfo = info; + updateConnection(); + } else if (mConnectingDevice == null && mDesiredDevice == null) { + // this is the case when we received an incoming connection + // from the sink, update both mConnectingDevice and mDesiredDevice + // then proceed to updateConnection() below + mConnectingDevice = mDesiredDevice = owner ? + info.getClientList().iterator().next() : info.getOwner(); + } + } + if (mConnectingDevice != null && mConnectingDevice == mDesiredDevice) { Slog.i(TAG, "Connected to Wifi display: " + mConnectingDevice.deviceName); @@ -712,6 +785,7 @@ final class WifiDisplayController implements DumpUtils.Dump { }); } } else { + mConnectedDeviceGroupInfo = null; disconnect(); // After disconnection for a group, for some reason we have a tendency @@ -910,6 +984,13 @@ final class WifiDisplayController implements DumpUtils.Dump { } handleConnectionChanged(networkInfo); + } else if (action.equals(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)) { + mThisDevice = (WifiP2pDevice) intent.getParcelableExtra( + WifiP2pManager.EXTRA_WIFI_P2P_DEVICE); + if (DEBUG) { + Slog.d(TAG, "Received WIFI_P2P_THIS_DEVICE_CHANGED_ACTION: mThisDevice= " + + mThisDevice); + } } } }; @@ -928,6 +1009,7 @@ final class WifiDisplayController implements DumpUtils.Dump { void onDisplayChanged(WifiDisplay display); void onDisplayConnected(WifiDisplay display, Surface surface, int width, int height, int flags); + void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo); void onDisplayDisconnected(); } } diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 79c1163..d3342dd 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -619,6 +619,37 @@ public class WifiNative { return doBooleanCommand("P2P_LISTEN " + timeout); } + public boolean p2pExtListen(boolean enable, int period, int interval) { + if (enable && interval < period) { + return false; + } + return doBooleanCommand("P2P_EXT_LISTEN" + + (enable ? (" " + period + " " + interval) : "")); + } + + public boolean p2pSetChannel(int lc, int oc) { + if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc); + + if (lc >=1 && lc <= 11) { + if (!doBooleanCommand("P2P_SET listen_channel " + lc)) { + return false; + } + } else if (lc != 0) { + return false; + } + + if (oc >= 1 && oc <= 165 ) { + int freq = (oc <= 14 ? 2407 : 5000) + oc * 5; + return doBooleanCommand("P2P_SET disallow_freq 1000-" + + (freq - 5) + "," + (freq + 5) + "-6000"); + } else if (oc == 0) { + /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */ + return doBooleanCommand("P2P_SET disallow_freq \"\""); + } + + return false; + } + public boolean p2pFlush() { return doBooleanCommand("P2P_FLUSH"); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index 737ab91..4988b92 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -30,6 +30,7 @@ import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.Handler; import android.os.Looper; @@ -430,6 +431,28 @@ public class WifiP2pManager { /** @hide */ public static final int START_WPS_SUCCEEDED = BASE + 64; + /** @hide */ + public static final int START_LISTEN = BASE + 65; + /** @hide */ + public static final int START_LISTEN_FAILED = BASE + 66; + /** @hide */ + public static final int START_LISTEN_SUCCEEDED = BASE + 67; + + /** @hide */ + public static final int STOP_LISTEN = BASE + 68; + /** @hide */ + public static final int STOP_LISTEN_FAILED = BASE + 69; + /** @hide */ + public static final int STOP_LISTEN_SUCCEEDED = BASE + 70; + + /** @hide */ + public static final int SET_CHANNEL = BASE + 71; + /** @hide */ + public static final int SET_CHANNEL_FAILED = BASE + 72; + /** @hide */ + public static final int SET_CHANNEL_SUCCEEDED = BASE + 73; + + /** * Create a new WifiP2pManager instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve @@ -667,6 +690,9 @@ public class WifiP2pManager { case DELETE_PERSISTENT_GROUP_FAILED: case SET_WFD_INFO_FAILED: case START_WPS_FAILED: + case START_LISTEN_FAILED: + case STOP_LISTEN_FAILED: + case SET_CHANNEL_FAILED: if (listener != null) { ((ActionListener) listener).onFailure(message.arg1); } @@ -689,6 +715,9 @@ public class WifiP2pManager { case DELETE_PERSISTENT_GROUP_SUCCEEDED: case SET_WFD_INFO_SUCCEEDED: case START_WPS_SUCCEEDED: + case START_LISTEN_SUCCEEDED: + case STOP_LISTEN_SUCCEEDED: + case SET_CHANNEL_SUCCEEDED: if (listener != null) { ((ActionListener) listener).onSuccess(); } @@ -955,6 +984,22 @@ public class WifiP2pManager { c.mAsyncChannel.sendMessage(REMOVE_GROUP, 0, c.putListener(listener)); } + /** @hide */ + public void listen(Channel c, boolean enable, ActionListener listener) { + checkChannel(c); + c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN, + 0, c.putListener(listener)); + } + + /** @hide */ + public void setWifiP2pChannels(Channel c, int lc, int oc, ActionListener listener) { + checkChannel(c); + Bundle p2pChannels = new Bundle(); + p2pChannels.putInt("lc", lc); + p2pChannels.putInt("oc", oc); + c.mAsyncChannel.sendMessage(SET_CHANNEL, 0, c.putListener(listener), p2pChannels); + } + /** * Start a Wi-Fi Protected Setup (WPS) session. * diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index 63b94a2..05196b8 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -48,6 +48,7 @@ import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Handler; @@ -628,6 +629,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case DhcpStateMachine.CMD_ON_QUIT: case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT: case SET_MIRACAST_MODE: + case WifiP2pManager.START_LISTEN: + case WifiP2pManager.STOP_LISTEN: + case WifiP2pManager.SET_CHANNEL: break; case WifiStateMachine.CMD_ENABLE_P2P: // Enable is lazy and has no response @@ -729,7 +733,16 @@ public class WifiP2pService extends IWifiP2pManager.Stub { replyToMessage(message, WifiP2pManager.START_WPS_FAILED, WifiP2pManager.P2P_UNSUPPORTED); break; - default: + case WifiP2pManager.START_LISTEN: + replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED, + WifiP2pManager.P2P_UNSUPPORTED); + break; + case WifiP2pManager.STOP_LISTEN: + replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED, + WifiP2pManager.P2P_UNSUPPORTED); + break; + + default: return NOT_HANDLED; } return HANDLED; @@ -1022,6 +1035,35 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case SET_MIRACAST_MODE: mWifiNative.setMiracastMode(message.arg1); break; + case WifiP2pManager.START_LISTEN: + if (DBG) logd(getName() + " start listen mode"); + mWifiNative.p2pFlush(); + if (mWifiNative.p2pExtListen(true, 500, 500)) { + replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); + } + break; + case WifiP2pManager.STOP_LISTEN: + if (DBG) logd(getName() + " stop listen mode"); + if (mWifiNative.p2pExtListen(false, 0, 0)) { + replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED); + } + mWifiNative.p2pFlush(); + break; + case WifiP2pManager.SET_CHANNEL: + Bundle p2pChannels = (Bundle) message.obj; + int lc = p2pChannels.getInt("lc", 0); + int oc = p2pChannels.getInt("oc", 0); + if (DBG) logd(getName() + " set listen and operating channel"); + if (mWifiNative.p2pSetChannel(lc, oc)) { + replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED); + } + break; default: return NOT_HANDLED; } @@ -1171,6 +1213,35 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mWifiNative.p2pGroupRemove(mGroup.getInterface()); } break; + case WifiP2pManager.START_LISTEN: + if (DBG) logd(getName() + " start listen mode"); + mWifiNative.p2pFlush(); + if (mWifiNative.p2pExtListen(true, 500, 500)) { + replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); + } + break; + case WifiP2pManager.STOP_LISTEN: + if (DBG) logd(getName() + " stop listen mode"); + if (mWifiNative.p2pExtListen(false, 0, 0)) { + replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED); + } + mWifiNative.p2pFlush(); + break; + case WifiP2pManager.SET_CHANNEL: + Bundle p2pChannels = (Bundle) message.obj; + int lc = p2pChannels.getInt("lc", 0); + int oc = p2pChannels.getInt("oc", 0); + if (DBG) logd(getName() + " set listen and operating channel"); + if (mWifiNative.p2pSetChannel(lc, oc)) { + replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED); + } + break; default: return NOT_HANDLED; } |