Merge "SurfaceTexture: enable RGB external textures"
diff --git a/Android.mk b/Android.mk
index 3b0fc59..7b8d0bc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -195,6 +195,7 @@
telephony/java/com/android/internal/telephony/ISms.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
+ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
telephony/java/com/android/internal/telephony/IExtendedNetworkService.aidl \
voip/java/android/net/sip/ISipSession.aidl \
voip/java/android/net/sip/ISipSessionListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index f88001d..cb5efa3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10406,8 +10406,8 @@
method public void setAudioEncodingBitRate(int);
method public void setAudioSamplingRate(int);
method public void setAudioSource(int) throws java.lang.IllegalStateException;
- method public void setAuxiliaryOutputFile(java.io.FileDescriptor);
- method public void setAuxiliaryOutputFile(java.lang.String);
+ method public deprecated void setAuxiliaryOutputFile(java.io.FileDescriptor);
+ method public deprecated void setAuxiliaryOutputFile(java.lang.String);
method public void setCamera(android.hardware.Camera);
method public void setCaptureRate(double);
method public void setLocation(float, float);
@@ -17951,11 +17951,12 @@
method public abstract android.view.textservice.SuggestionsInfo getSuggestions(android.view.textservice.TextInfo, int, java.lang.String);
method public android.view.textservice.SuggestionsInfo[] getSuggestionsMultiple(android.view.textservice.TextInfo[], java.lang.String, int, boolean);
method public final android.os.IBinder onBind(android.content.Intent);
- field public static final java.lang.String SERVICE_INTERFACE;
+ field public static final java.lang.String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService";
}
public class SpellCheckerSession {
method public void close();
+ method public android.view.textservice.SpellCheckerInfo getSpellChecker();
method public void getSuggestions(android.view.textservice.TextInfo, int);
method public void getSuggestions(android.view.textservice.TextInfo[], int, boolean);
method public boolean isSessionDisconnected();
@@ -22231,6 +22232,7 @@
method public final android.view.View findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence);
method protected boolean fitSystemWindows(android.graphics.Rect);
+ method public boolean fitsSystemWindows();
method public android.view.View focusSearch(int);
method public void forceLayout();
method public float getAlpha();
@@ -22472,6 +22474,7 @@
method public void setEnabled(boolean);
method public void setFadingEdgeLength(int);
method public void setFilterTouchesWhenObscured(boolean);
+ method public void setFitsSystemWindows(boolean);
method public void setFocusable(boolean);
method public void setFocusableInTouchMode(boolean);
method public void setHapticFeedbackEnabled(boolean);
@@ -24054,6 +24057,8 @@
method public android.content.ComponentName getComponent();
method public java.lang.String getId();
method public java.lang.String getPackageName();
+ method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
+ method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -24072,8 +24077,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int RESULT_ATTR_IN_THE_DICTIONARY = 1; // 0x1
- field public static final int RESULT_ATTR_LOOKS_TYPO = 4; // 0x4
- field public static final int RESULT_ATTR_SUGGESTIONS_AVAILABLE = 2; // 0x2
+ field public static final int RESULT_ATTR_LOOKS_TYPO = 2; // 0x2
}
public final class TextInfo implements android.os.Parcelable {
@@ -24089,8 +24093,7 @@
}
public final class TextServicesManager {
- method public android.view.textservice.SpellCheckerInfo getCurrentSpellChecker(java.util.Locale);
- method public android.service.textservice.SpellCheckerSession newSpellCheckerSession(android.view.textservice.SpellCheckerInfo, java.util.Locale, android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener);
+ method public android.service.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.service.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
}
}
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index b42f1c5..34f0a64 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -803,9 +803,9 @@
printf("type '%s':\n", kMimeTypes[k]);
Vector<CodecCapabilities> results;
+ // will retrieve hardware and software codecs
CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
true, // queryDecoders
- false, // hwCodecOnly
&results), (status_t)OK);
for (size_t i = 0; i < results.size(); ++i) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6289730..a99cec2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -59,6 +59,8 @@
import android.net.Uri;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.IWifiP2pManager;
+import android.net.wifi.p2p.WifiP2pManager;
import android.nfc.NfcManager;
import android.os.Binder;
import android.os.Bundle;
@@ -438,6 +440,13 @@
return new WifiManager(service, ctx.mMainThread.getHandler());
}});
+ registerService(WIFI_P2P_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(WIFI_P2P_SERVICE);
+ IWifiP2pManager service = IWifiP2pManager.Stub.asInterface(b);
+ return new WifiP2pManager(service);
+ }});
+
registerService(WINDOW_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
return WindowManagerImpl.getDefault(ctx.mPackageInfo.mCompatibilityInfo);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0a2253c8..cdda910 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1572,6 +1572,17 @@
public static final String WIFI_SERVICE = "wifi";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.wifi.p2p.WifiP2pManager} for handling management of
+ * Wi-Fi p2p.
+ *
+ * @see #getSystemService
+ * @see android.net.wifi.p2p.WifiP2pManager
+ * @hide
+ */
+ public static final String WIFI_P2P_SERVICE = "wifip2p";
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* {@link android.media.AudioManager} for handling management of volume,
* ringer modes and audio routing.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce6f697..a564d97 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -653,6 +653,17 @@
}
}
+ /**
+ * {@hide}
+ */
+ public int setUsbTethering(boolean enable) {
+ try {
+ return mService.setUsbTethering(enable);
+ } catch (RemoteException e) {
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+ }
+
/** {@hide} */
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index d95fc8d..b1d99a4 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -88,6 +88,8 @@
String[] getTetherableBluetoothRegexs();
+ int setUsbTethering(boolean enable);
+
void requestNetworkTransitionWakelock(in String forWhom);
void reportInetCondition(int networkType, int percentage);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f230526..1174e3b 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -180,7 +180,7 @@
/**
* Stop Wifi Access Point
*/
- void stopAccessPoint();
+ void stopAccessPoint(String wlanIface);
/**
* Set Access Point config
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index 437e553..166b21b 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -61,8 +61,8 @@
View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
if (checkboxView != null && checkboxView instanceof Checkable) {
((Checkable) checkboxView).setChecked(mChecked);
-
- sendAccessibilityEventForView(checkboxView);
+ // Post this so this view is bound and attached when firing the event.
+ postSendAccessibilityEventForView(checkboxView);
}
syncSummaryView(view);
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index f681526..3dbd522 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -102,8 +102,8 @@
View checkableView = view.findViewById(com.android.internal.R.id.switchWidget);
if (checkableView != null && checkableView instanceof Checkable) {
((Checkable) checkableView).setChecked(mChecked);
-
- sendAccessibilityEventForView(checkableView);
+ // Post this so this view is bound and attached when firing the event.
+ postSendAccessibilityEventForView(checkableView);
if (checkableView instanceof Switch) {
final Switch switchView = (Switch) checkableView;
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
index 8e21c4c..55ef108 100644
--- a/core/java/android/preference/TwoStatePreference.java
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -16,7 +16,6 @@
package android.preference;
-import android.app.Service;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
@@ -39,28 +38,20 @@
private CharSequence mSummaryOff;
boolean mChecked;
private boolean mSendAccessibilityEventViewClickedType;
- private AccessibilityManager mAccessibilityManager;
private boolean mDisableDependentsState;
+ private SendAccessibilityEventTypeViewClicked mSendAccessibilityEventTypeViewClicked;
+
public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
- mAccessibilityManager =
- (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
}
public TwoStatePreference(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mAccessibilityManager =
- (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
+ this(context, attrs, 0);
}
public TwoStatePreference(Context context) {
- super(context);
-
- mAccessibilityManager =
- (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
+ this(context, null);
}
@Override
@@ -198,20 +189,23 @@
}
/**
- * Send an accessibility event for the given view if appropriate
+ * Post send an accessibility event for the given view if appropriate.
+ *
* @param view View that should send the event
*/
- void sendAccessibilityEventForView(View view) {
+ void postSendAccessibilityEventForView(View view) {
// send an event to announce the value change of the state. It is done here
// because clicking a preference does not immediately change the checked state
// for example when enabling the WiFi
- if (mSendAccessibilityEventViewClickedType &&
- mAccessibilityManager.isEnabled() &&
- view.isEnabled()) {
+ if (mSendAccessibilityEventViewClickedType
+ && AccessibilityManager.getInstance(getContext()).isEnabled()
+ && view.isEnabled()) {
mSendAccessibilityEventViewClickedType = false;
-
- int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;
- view.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
+ if (mSendAccessibilityEventTypeViewClicked == null) {
+ mSendAccessibilityEventTypeViewClicked = new SendAccessibilityEventTypeViewClicked();
+ }
+ mSendAccessibilityEventTypeViewClicked.mView = view;
+ view.post(mSendAccessibilityEventTypeViewClicked);
}
}
@@ -306,4 +300,13 @@
}
};
}
+
+ private final class SendAccessibilityEventTypeViewClicked implements Runnable {
+ private View mView;
+
+ @Override
+ public void run() {
+ mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+ }
+ }
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 382fcf3..e23d6f3 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -184,6 +184,16 @@
public static final String VOICEMAIL_URI = "voicemail_uri";
/**
+ * Whether this item has been read or otherwise consumed by the user.
+ * <p>
+ * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
+ * existence of the entry, this implies the user has interacted with the entry.
+ * <P>Type: INTEGER (boolean)</P>
+ * @hide
+ */
+ public static final String IS_READ = "is_read";
+
+ /**
* Adds a call to the call log.
*
* @param ci the CallerInfo object to get the target contact from. Can be null
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 4a719ec..a0f1eec 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6514,6 +6514,32 @@
public static final String SUMMARY_COUNT = "summ_count";
/**
+ * A boolean query parameter that can be used with {@link Groups#CONTENT_SUMMARY_URI}.
+ * It will additionally return {@link #SUMMARY_GROUP_COUNT_PER_ACCOUNT}.
+ *
+ * @hide
+ */
+ public static final String PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT =
+ "return_group_count_per_account";
+
+ /**
+ * The total number of groups of the account that a group belongs to.
+ * This column is available only when the parameter
+ * {@link #PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT} is specified in
+ * {@link Groups#CONTENT_SUMMARY_URI}.
+ *
+ * For example, when the account "A" has two groups "group1" and "group2", and the account
+ * "B" has a group "group3", the rows for "group1" and "group2" return "2" and the row for
+ * "group3" returns "1" for this column.
+ *
+ * Note: This counts only non-favorites, non-auto-add, and not deleted groups.
+ *
+ * Type: INTEGER
+ * @hide
+ */
+ public static final String SUMMARY_GROUP_COUNT_PER_ACCOUNT = "group_count_per_account";
+
+ /**
* The total number of {@link Contacts} that have both
* {@link CommonDataKinds.GroupMembership} in this group, and also have phone numbers.
* Read-only value that is only present when querying
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1cd46de..6ebbe7c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3935,6 +3935,10 @@
/** Timeout in milliseconds to wait for NTP server. {@hide} */
public static final String NTP_TIMEOUT = "ntp_timeout";
+ /** Autofill server address (Used in WebView/browser). {@hide} */
+ public static final String WEB_AUTOFILL_QUERY_URL =
+ "web_autofill_query_url";
+
/**
* @hide
*/
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 2ad7395..2e5a495 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -128,6 +128,12 @@
*/
public static final String NEW = Calls.NEW;
/**
+ * Whether this item has been read or otherwise consumed by the user.
+ * <P>Type: INTEGER (boolean)</P>
+ * @hide
+ */
+ public static final String IS_READ = Calls.IS_READ;
+ /**
* The mail box state of the voicemail. This field is currently not used by the system.
* <P> Possible values: {@link #STATE_INBOX}, {@link #STATE_DELETED},
* {@link #STATE_UNDELETED}.
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
new file mode 100644
index 0000000..ae91465
--- /dev/null
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2011 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.server;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.io.PrintWriter;
+
+/**
+ * Bluetooth Adapter StateMachine
+ * All the states are at the same level, ie, no hierarchy.
+ * (BluetootOn)
+ * | ^
+ * TURN_OFF | | BECOME_PAIRABLE
+ * AIRPLANE_MODE_ON | |
+ * V |
+ * (Switching)
+ * | ^
+ * BECOME_NON_PAIRABLE& | | TURN_ON(_CONTINUE)/TURN_ON_FOR_PRIVILEGED
+ * ALL_DEVICES_DISCONNECTED | |
+ * V |
+ * (HotOff)
+ * / ^
+ * / | SERVICE_RECORD_LOADED
+ * | |
+ * TURN_COLD | (Warmup)
+ * \ ^
+ * \ | TURN_HOT/TURN_ON
+ * | | AIRPLANE_MODE_OFF(when Bluetooth was on before)
+ * V |
+ * (PowerOff) <----- initial state
+ *
+ */
+final class BluetoothAdapterStateMachine extends StateMachine {
+ private static final String TAG = "BluetoothAdapterStateMachine";
+ private static final boolean DBG = false;
+
+ // Message(what) to take an action
+ //
+ // We get this message when user tries to turn on BT
+ public static final int USER_TURN_ON = 1;
+ // We get this message when user tries to turn off BT
+ public static final int USER_TURN_OFF = 2;
+
+ // Message(what) to report a event that the state machine need to respond to
+ //
+ // Event indicates sevice records have been loaded
+ public static final int SERVICE_RECORD_LOADED = 51;
+ // Event indicates all the remote Bluetooth devices has been disconnected
+ public static final int ALL_DEVICES_DISCONNECTED = 52;
+ // Event indicates the Bluetooth is connectable
+ public static final int BECOME_PAIRABLE = 53;
+ // Event indicates the Bluetooth is non-connectable.
+ public static final int BECOME_NON_PAIRABLE = 54;
+ // Event indicates airplane mode is turned on
+ public static final int AIRPLANE_MODE_ON = 55;
+ // Event indicates airplane mode is turned off
+ public static final int AIRPLANE_MODE_OFF = 56;
+
+ // private internal messages
+ //
+ // Turn on Bluetooth Module, Load firmware, and do all the preparation
+ // needed to get the Bluetooth Module ready but keep it not discoverable
+ // and not connectable. This way the Bluetooth Module can be quickly
+ // switched on if needed
+ private static final int TURN_HOT = 101;
+ // USER_TURN_ON is changed to TURN_ON_CONTINUE after we broadcast the
+ // state change intent so that we will not broadcast the intent again in
+ // other state
+ private static final int TURN_ON_CONTINUE = 102;
+ // Unload firmware, turning off Bluetooth module power
+ private static final int TURN_COLD = 103;
+ // For NFC, turn on bluetooth for certain process
+ private static final int TURN_ON_FOR_PRIVILEGED = 104;
+
+ private Context mContext;
+ private BluetoothService mBluetoothService;
+ private BluetoothEventLoop mEventLoop;
+
+ private BluetoothOn mBluetoothOn;
+ private Switching mSwitching;
+ private HotOff mHotOff;
+ private WarmUp mWarmUp;
+ private PowerOff mPowerOff;
+
+ // this is the BluetoothAdapter state that reported externally
+ private int mPublicState;
+
+ BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,
+ BluetoothAdapter bluetoothAdapter) {
+ super(TAG);
+ mContext = context;
+ mBluetoothService = bluetoothService;
+ mEventLoop = new BluetoothEventLoop(context, bluetoothAdapter, bluetoothService, this);
+
+ mBluetoothOn = new BluetoothOn();
+ mSwitching = new Switching();
+ mHotOff = new HotOff();
+ mWarmUp = new WarmUp();
+ mPowerOff = new PowerOff();
+
+ addState(mBluetoothOn);
+ addState(mSwitching);
+ addState(mHotOff);
+ addState(mWarmUp);
+ addState(mPowerOff);
+ setInitialState(mPowerOff);
+ mPublicState = BluetoothAdapter.STATE_OFF;
+
+ if (mContext.getResources().getBoolean
+ (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
+ sendMessage(TURN_HOT);
+ }
+ }
+
+ /**
+ * Bluetooth module's power is off, firmware is not loaded.
+ */
+ private class PowerOff extends State {
+ private boolean mPersistSwitchOn = false;
+
+ @Override
+ public void enter() {
+ if (DBG) log("Enter PowerOff: " + mPersistSwitchOn);
+ mPersistSwitchOn = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("PowerOff process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case USER_TURN_ON:
+ // starts turning on BT module, broadcast this out
+ transitionTo(mWarmUp);
+ broadcastState(BluetoothAdapter.STATE_TURNING_ON);
+ if (prepareBluetooth()) {
+ // this is user request, save the setting
+ if ((Boolean) message.obj) {
+ mPersistSwitchOn = true;
+ }
+ // We will continue turn the BT on all the way to the BluetoothOn state
+ deferMessage(obtainMessage(TURN_ON_CONTINUE));
+ } else {
+ Log.e(TAG, "failed to prepare bluetooth, abort turning on");
+ transitionTo(mPowerOff);
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ }
+ break;
+ case TURN_HOT:
+ if (prepareBluetooth()) {
+ transitionTo(mWarmUp);
+ }
+ break;
+ case AIRPLANE_MODE_OFF:
+ if (getBluetoothPersistedSetting()) {
+ // starts turning on BT module, broadcast this out
+ transitionTo(mWarmUp);
+ broadcastState(BluetoothAdapter.STATE_TURNING_ON);
+ if (prepareBluetooth()) {
+ // We will continue turn the BT on all the way to the BluetoothOn state
+ deferMessage(obtainMessage(TURN_ON_CONTINUE));
+ transitionTo(mWarmUp);
+ } else {
+ Log.e(TAG, "failed to prepare bluetooth, abort turning on");
+ transitionTo(mPowerOff);
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ }
+ }
+ break;
+ case AIRPLANE_MODE_ON: // ignore
+ case USER_TURN_OFF: // ignore
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+
+ /**
+ * Turn on Bluetooth Module, Load firmware, and do all the preparation
+ * needed to get the Bluetooth Module ready but keep it not discoverable
+ * and not connectable.
+ * The last step of this method sets up the local service record DB.
+ * There will be a event reporting the status of the SDP setup.
+ */
+ private boolean prepareBluetooth() {
+ if (mBluetoothService.enableNative() != 0) {
+ return false;
+ }
+
+ // try to start event loop, give 2 attempts
+ int retryCount = 2;
+ boolean eventLoopStarted = false;
+ while ((retryCount-- > 0) && !eventLoopStarted) {
+ mEventLoop.start();
+ // it may take a moment for the other thread to do its
+ // thing. Check periodically for a while.
+ int pollCount = 5;
+ while ((pollCount-- > 0) && !eventLoopStarted) {
+ if (mEventLoop.isEventLoopRunning()) {
+ eventLoopStarted = true;
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ if (!eventLoopStarted) {
+ mBluetoothService.disableNative();
+ return false;
+ }
+
+ // get BluetoothService ready
+ if (!mBluetoothService.prepareBluetooth()) {
+ mEventLoop.stop();
+ mBluetoothService.disableNative();
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Turning on Bluetooth module's power, loading firmware, starting
+ * event loop thread to listen on Bluetooth module event changes.
+ */
+ private class WarmUp extends State {
+
+ @Override
+ public void enter() {
+ if (DBG) log("Enter WarmUp");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("WarmUp process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case SERVICE_RECORD_LOADED:
+ transitionTo(mHotOff);
+ break;
+ case USER_TURN_ON: // handle this at HotOff state
+ case TURN_ON_CONTINUE: // Once in HotOff state, continue turn bluetooth
+ // on to the BluetoothOn state
+ case AIRPLANE_MODE_ON:
+ case AIRPLANE_MODE_OFF:
+ deferMessage(message);
+ break;
+ case USER_TURN_OFF: // ignore
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+
+ }
+
+ /**
+ * Bluetooth Module has powered, firmware loaded, event loop started,
+ * SDP loaded, but the modules stays non-discoverable and
+ * non-connectable.
+ */
+ private class HotOff extends State {
+ private boolean mPersistSwitchOn = false;
+
+ @Override
+ public void enter() {
+ if (DBG) log("Enter HotOff: " + mPersistSwitchOn);
+ mPersistSwitchOn = false;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("HotOff process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case USER_TURN_ON:
+ if ((Boolean) message.obj) {
+ mPersistSwitchOn = true;
+ }
+ // let it fall to TURN_ON_CONTINUE:
+ case TURN_ON_CONTINUE:
+ mBluetoothService.switchConnectable(true);
+ transitionTo(mSwitching);
+ broadcastState(BluetoothAdapter.STATE_TURNING_ON);
+ break;
+ case AIRPLANE_MODE_ON:
+ case TURN_COLD:
+ mBluetoothService.shutoffBluetooth();
+ mEventLoop.stop();
+ transitionTo(mPowerOff);
+ // ASSERT no support of config_bluetooth_adapter_quick_switch
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ break;
+ case AIRPLANE_MODE_OFF:
+ if (getBluetoothPersistedSetting()) {
+ mBluetoothService.switchConnectable(true);
+ transitionTo(mSwitching);
+ broadcastState(BluetoothAdapter.STATE_TURNING_ON);
+ }
+ break;
+ case USER_TURN_OFF: // ignore
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+
+ }
+
+ private class Switching extends State {
+
+ @Override
+ public void enter() {
+ int what = getCurrentMessage().what;
+ if (DBG) log("Enter Switching: " + what);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("Switching process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case BECOME_PAIRABLE:
+ if (mPowerOff.mPersistSwitchOn || mHotOff.mPersistSwitchOn) {
+ persistSwitchSetting(true);
+ mPowerOff.mPersistSwitchOn = mHotOff.mPersistSwitchOn = false;
+ }
+ String[] propVal = {"Pairable", mBluetoothService.getProperty("Pairable")};
+ mEventLoop.onPropertyChanged(propVal);
+
+ // run bluetooth now that it's turned on
+ mBluetoothService.runBluetooth();
+ transitionTo(mBluetoothOn);
+ broadcastState(BluetoothAdapter.STATE_ON);
+ break;
+ case BECOME_NON_PAIRABLE:
+ if (mBluetoothService.getAdapterConnectionState() ==
+ BluetoothAdapter.STATE_DISCONNECTED) {
+ transitionTo(mHotOff);
+ finishSwitchingOff();
+ }
+ break;
+ case ALL_DEVICES_DISCONNECTED:
+ if (mBluetoothService.getScanMode() == BluetoothAdapter.SCAN_MODE_NONE) {
+ transitionTo(mHotOff);
+ finishSwitchingOff();
+ }
+ break;
+ case USER_TURN_ON:
+ case AIRPLANE_MODE_OFF:
+ case AIRPLANE_MODE_ON:
+ case USER_TURN_OFF:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+
+ private void finishSwitchingOff() {
+ if (mBluetoothOn.mPersistBluetoothOff) {
+ persistSwitchSetting(false);
+ mBluetoothOn.mPersistBluetoothOff = false;
+ }
+ mBluetoothService.finishDisable();
+ if (mContext.getResources().getBoolean
+ (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ } else {
+ deferMessage(obtainMessage(TURN_COLD));
+ }
+ }
+ }
+
+ private class BluetoothOn extends State {
+ private boolean mPersistBluetoothOff = false;
+
+ @Override
+ public void enter() {
+ if (DBG) log("Enter BluetoothOn: " + mPersistBluetoothOff);
+ mPersistBluetoothOff = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log("BluetoothOn process message: " + message.what);
+
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case USER_TURN_OFF:
+ if ((Boolean) message.obj) {
+ mPersistBluetoothOff = true;
+ }
+ // let it fall through to AIRPLANE_MODE_ON
+ case AIRPLANE_MODE_ON:
+ transitionTo(mSwitching);
+ broadcastState(BluetoothAdapter.STATE_TURNING_OFF);
+ mBluetoothService.switchConnectable(false);
+ mBluetoothService.disconnectDevices();
+ // we turn all the way to PowerOff with AIRPLANE_MODE_ON
+ if (message.what == AIRPLANE_MODE_ON) {
+ deferMessage(obtainMessage(AIRPLANE_MODE_ON));
+ }
+ break;
+ case AIRPLANE_MODE_OFF: // ignore
+ case USER_TURN_ON: // ignore
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return retValue;
+ }
+
+ }
+
+ /**
+ * Return the public BluetoothAdapter state
+ */
+ int getBluetoothAdapterState() {
+ return mPublicState;
+ }
+
+ BluetoothEventLoop getBluetoothEventLoop() {
+ return mEventLoop;
+ }
+
+ private void persistSwitchSetting(boolean setOn) {
+ long origCallerIdentityToken = Binder.clearCallingIdentity();
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BLUETOOTH_ON,
+ setOn ? 1 : 0);
+ Binder.restoreCallingIdentity(origCallerIdentityToken);
+ }
+
+ private boolean getBluetoothPersistedSetting() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ return (Settings.Secure.getInt(contentResolver,
+ Settings.Secure.BLUETOOTH_ON, 0) > 0);
+ }
+
+ private void broadcastState(int newState) {
+
+ if (DBG) log("Bluetooth state " + mPublicState + " -> " + newState);
+ if (mPublicState == newState) {
+ return;
+ }
+
+ Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mPublicState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mPublicState = newState;
+
+ mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
+ }
+
+ private void dump(PrintWriter pw) {
+ IState currentState = getCurrentState();
+ if (currentState == mPowerOff) {
+ pw.println("Bluetooth OFF - power down\n");
+ } else if (currentState == mWarmUp) {
+ pw.println("Bluetooth OFF - warm up\n");
+ } else if (currentState == mHotOff) {
+ pw.println("Bluetooth OFF - hot but off\n");
+ } else if (currentState == mSwitching) {
+ pw.println("Bluetooth Switching\n");
+ } else if (currentState == mBluetoothOn) {
+ pw.println("Bluetooth ON\n");
+ } else {
+ pw.println("ERROR: Bluetooth UNKNOWN STATE ");
+ }
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index f345a6a..107a2a9 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -52,6 +52,7 @@
private final HashMap<String, Integer> mAuthorizationAgentRequestData;
private final BluetoothService mBluetoothService;
private final BluetoothAdapter mAdapter;
+ private final BluetoothAdapterStateMachine mBluetoothState;
private BluetoothA2dp mA2dp;
private BluetoothInputDevice mInputDevice;
private final Context mContext;
@@ -107,9 +108,11 @@
private static native void classInitNative();
/* package */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
- BluetoothService bluetoothService) {
+ BluetoothService bluetoothService,
+ BluetoothAdapterStateMachine bluetoothState) {
mBluetoothService = bluetoothService;
mContext = context;
+ mBluetoothState = bluetoothState;
mPasskeyAgentRequestData = new HashMap<String, Integer>();
mAuthorizationAgentRequestData = new HashMap<String, Integer>();
mAdapter = adapter;
@@ -299,8 +302,8 @@
/**
* Called by native code on a PropertyChanged signal from
- * org.bluez.Adapter. This method is also called from Java at
- * {@link BluetoothService.EnableThread#run()} to set the "Pairable"
+ * org.bluez.Adapter. This method is also called from
+ * {@link BluetoothAdapterStateMachine} to set the "Pairable"
* property when Bluetooth is enabled.
*
* @param propValues a string array containing the key and one or more
@@ -334,6 +337,15 @@
return;
adapterProperties.setProperty(name, propValues[1]);
+
+ if (name.equals("Pairable")) {
+ if (pairable.equals("true")) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_PAIRABLE);
+ } else {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECOME_NON_PAIRABLE);
+ }
+ }
+
int mode = BluetoothService.bluezStringToScanMode(
pairable.equals("true"),
discoverable.equals("true"));
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index d68d8ba..34f1971 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -91,7 +91,7 @@
private BluetoothPan mPan;
private boolean mIsAirplaneSensitive;
private boolean mIsAirplaneToggleable;
- private int mBluetoothState;
+ private BluetoothAdapterStateMachine mBluetoothState;
private boolean mRestart = false; // need to call enable() after disable()
private boolean mIsDiscovering;
private int[] mAdapterSdpHandles;
@@ -111,9 +111,8 @@
private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
- private static final int MESSAGE_FINISH_DISABLE = 1;
- private static final int MESSAGE_UUID_INTENT = 2;
- private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3;
+ private static final int MESSAGE_UUID_INTENT = 1;
+ private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
// The time (in millisecs) to delay the pairing attempt after the first
// auto pairing attempt fails. We use an exponential delay with
@@ -206,7 +205,6 @@
disableNative();
}
- mBluetoothState = BluetoothAdapter.STATE_OFF;
mIsDiscovering = false;
mBondState = new BluetoothBondState(context, this);
@@ -306,7 +304,9 @@
public synchronized void initAfterRegistration() {
mAdapter = BluetoothAdapter.getDefaultAdapter();
- mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
+ mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter);
+ mBluetoothState.start();
+ mEventLoop = mBluetoothState.getBluetoothEventLoop();
}
public synchronized void initAfterA2dpRegistration() {
@@ -329,16 +329,16 @@
}
private boolean isEnabledInternal() {
- return mBluetoothState == BluetoothAdapter.STATE_ON;
+ return (getBluetoothStateInternal() == BluetoothAdapter.STATE_ON);
}
public int getBluetoothState() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothState;
+ return getBluetoothStateInternal();
}
int getBluetoothStateInternal() {
- return mBluetoothState;
+ return mBluetoothState.getBluetoothAdapterState();
}
/**
@@ -356,7 +356,9 @@
public synchronized boolean disable(boolean saveSetting) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
- switch (mBluetoothState) {
+ int adapterState = getBluetoothStateInternal();
+
+ switch (adapterState) {
case BluetoothAdapter.STATE_OFF:
return true;
case BluetoothAdapter.STATE_ON:
@@ -364,27 +366,12 @@
default:
return false;
}
- if (mEnableThread != null && mEnableThread.isAlive()) {
- return false;
- }
- setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
-
- if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
- setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE,
- BluetoothPanProfileHandler.NAP_BRIDGE);
-
- // Allow 3 seconds for profiles to gracefully disconnect
- // TODO: Introduce a callback mechanism so that each profile can notify
- // BluetoothService when it is done shutting down
- disconnectDevices();
-
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting);
return true;
}
- private synchronized void disconnectDevices() {
+ synchronized void disconnectDevices() {
// Disconnect devices handled by BluetoothService.
for (BluetoothDevice device: getConnectedInputDevices()) {
disconnectInputDevice(device);
@@ -395,14 +382,11 @@
}
}
- private synchronized void finishDisable(boolean saveSetting) {
- if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
- return;
- }
- mEventLoop.stop();
- tearDownNativeDataNative();
- disableNative();
-
+ /**
+ * The Bluetooth has been turned off, but hot. Do bonding, profile,
+ * and internal cleanup
+ */
+ synchronized void finishDisable() {
// mark in progress bondings as cancelled
for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
@@ -430,12 +414,6 @@
mAdapterUuids = null;
mAdapterSdpHandles = null;
- if (saveSetting) {
- persistBluetoothOnSetting(false);
- }
-
- setBluetoothState(BluetoothAdapter.STATE_OFF);
-
// Log bluetooth off to battery stats.
long ident = Binder.clearCallingIdentity();
try {
@@ -451,6 +429,18 @@
}
}
+ /**
+ * power off Bluetooth
+ */
+ synchronized void shutoffBluetooth() {
+ tearDownNativeDataNative();
+ disableNative();
+ if (mRestart) {
+ mRestart = false;
+ enable();
+ }
+ }
+
/** Bring up BT and persist BT on in settings */
public boolean enable() {
return enable(true);
@@ -471,21 +461,29 @@
if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
return false;
}
- if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
+ return true;
+ }
+
+ /**
+ * Turn on Bluetooth Module, Load firmware, and do all the preparation
+ * needed to get the Bluetooth Module ready but keep it not discoverable
+ * and not connectable.
+ */
+ /* package */ synchronized boolean prepareBluetooth() {
+ if (!setupNativeDataNative()) {
return false;
}
- if (mEnableThread != null && mEnableThread.isAlive()) {
- return false;
- }
- setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
- mEnableThread = new EnableThread(saveSetting);
- mEnableThread.start();
+ mIsDiscovering = false;
+
+ switchConnectable(false);
+ updateSdpRecords();
return true;
}
/** Forcibly restart Bluetooth if it is on */
/* package */ synchronized void restart() {
- if (mBluetoothState != BluetoothAdapter.STATE_ON) {
+ if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
return;
}
mRestart = true;
@@ -494,30 +492,10 @@
}
}
- private synchronized void setBluetoothState(int state) {
- if (state == mBluetoothState) {
- return;
- }
-
- if (DBG) Log.d(TAG, "Bluetooth state " + mBluetoothState + " -> " + state);
-
- Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
- intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
- intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- mBluetoothState = state;
-
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
-
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_FINISH_DISABLE:
- finishDisable(msg.arg1 != 0);
- break;
case MESSAGE_UUID_INTENT:
String address = (String)msg.obj;
if (address != null) {
@@ -545,65 +523,6 @@
}
};
- private EnableThread mEnableThread;
-
- private class EnableThread extends Thread {
- private final boolean mSaveSetting;
- public EnableThread(boolean saveSetting) {
- mSaveSetting = saveSetting;
- }
- public void run() {
- boolean res = (enableNative() == 0);
- if (res) {
- int retryCount = 2;
- boolean running = false;
- while ((retryCount-- > 0) && !running) {
- mEventLoop.start();
- // it may take a moment for the other thread to do its
- // thing. Check periodically for a while.
- int pollCount = 5;
- while ((pollCount-- > 0) && !running) {
- if (mEventLoop.isEventLoopRunning()) {
- running = true;
- break;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {}
- }
- }
- if (!running) {
- Log.e(TAG, "bt EnableThread giving up");
- res = false;
- disableNative();
- }
- }
-
- if (res) {
- if (!setupNativeDataNative()) {
- return;
- }
- if (mSaveSetting) {
- persistBluetoothOnSetting(true);
- }
-
- mIsDiscovering = false;
- mBondState.readAutoPairingData();
- mBondState.initBondState();
- initProfileState();
-
- // This should be the last step of the the enable thread.
- // Because this adds SDP records which asynchronously
- // broadcasts the Bluetooth On State in updateBluetoothState.
- // So we want all internal state setup before this.
- updateSdpRecords();
- } else {
- setBluetoothState(BluetoothAdapter.STATE_OFF);
- }
- mEnableThread = null;
- }
- }
-
private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
//Register SDP records.
int[] svcIdentifiers = new int[uuids.size()];
@@ -650,38 +569,37 @@
* for adapter comes in with UUID property.
* @param uuidsThe uuids of adapter as reported by Bluez.
*/
- synchronized void updateBluetoothState(String uuids) {
- if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) {
- ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
+ /*package*/ synchronized void updateBluetoothState(String uuids) {
+ ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
- if (mAdapterUuids != null &&
- BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
- setBluetoothState(BluetoothAdapter.STATE_ON);
- autoConnect();
- String[] propVal = {"Pairable", getProperty("Pairable")};
- mEventLoop.onPropertyChanged(propVal);
-
- // Log bluetooth on to battery stats.
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteBluetoothOn();
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
- disable(false);
- }
- }
+ if (mAdapterUuids != null &&
+ BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED);
}
}
- private void persistBluetoothOnSetting(boolean bluetoothOn) {
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
- bluetoothOn ? 1 : 0);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ /**
+ * This method is called immediately after Bluetooth module is turned on.
+ * It starts auto-connection and places bluetooth on sign onto the battery
+ * stats
+ */
+ /*package*/ void runBluetooth() {
+ mIsDiscovering = false;
+ mBondState.readAutoPairingData();
+ mBondState.initBondState();
+ initProfileState();
+
+ autoConnect();
+
+ // Log bluetooth on to battery stats.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteBluetoothOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/*package*/ synchronized boolean attemptAutoPair(String address) {
@@ -818,6 +736,26 @@
public synchronized boolean setScanMode(int mode, int duration) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
"Need WRITE_SECURE_SETTINGS permission");
+ return setScanMode(mode, duration, true);
+ }
+
+ /**
+ * @param on true set the local Bluetooth module to be connectable
+ * but not dicoverable
+ * false set the local Bluetooth module to be not connectable
+ * and not dicoverable
+ */
+ /*package*/ synchronized void switchConnectable(boolean on) {
+ if (on) {
+ // 0 is a dummy value, does not apply for SCAN_MODE_CONNECTABLE
+ setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, 0, false);
+ } else {
+ // 0 is a dummy value, does not apply for SCAN_MODE_NONE
+ setScanMode(BluetoothAdapter.SCAN_MODE_NONE, 0, false);
+ }
+ }
+
+ private synchronized boolean setScanMode(int mode, int duration, boolean allowOnlyInOnState) {
boolean pairable;
boolean discoverable;
@@ -839,9 +777,15 @@
Log.w(TAG, "Requested invalid scan mode " + mode);
return false;
}
- setPropertyBoolean("Pairable", pairable);
- setPropertyBoolean("Discoverable", discoverable);
+ if (allowOnlyInOnState) {
+ setPropertyBoolean("Pairable", pairable);
+ setPropertyBoolean("Discoverable", discoverable);
+ } else {
+ // allowed to set the property through native layer directly
+ setAdapterPropertyBooleanNative("Pairable", pairable ? 1 : 0);
+ setAdapterPropertyBooleanNative("Discoverable", discoverable ? 1 : 0);
+ }
return true;
}
@@ -1569,14 +1513,10 @@
ContentResolver resolver = context.getContentResolver();
// Query the airplane mode from Settings.System just to make sure that
// some random app is not sending this intent and disabling bluetooth
- boolean enabled = !isAirplaneModeOn();
- // If bluetooth is currently expected to be on, then enable or disable bluetooth
- if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
- if (enabled) {
- enable(false);
- } else {
- disable(false);
- }
+ if (isAirplaneModeOn()) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON);
+ } else {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF);
}
} else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
@@ -1650,8 +1590,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dumpBluetoothState(pw);
- if (mBluetoothState != BluetoothAdapter.STATE_ON) {
+ if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
return;
}
@@ -1840,25 +1779,6 @@
}
}
- private void dumpBluetoothState(PrintWriter pw) {
- switch(mBluetoothState) {
- case BluetoothAdapter.STATE_OFF:
- pw.println("Bluetooth OFF\n");
- break;
- case BluetoothAdapter.STATE_TURNING_ON:
- pw.println("Bluetooth TURNING ON\n");
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- pw.println("Bluetooth TURNING OFF\n");
- break;
- case BluetoothAdapter.STATE_ON:
- pw.println("Bluetooth ON\n");
- break;
- default:
- pw.println("Bluetooth UNKNOWN STATE " + mBluetoothState);
- }
- }
-
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
@@ -2389,7 +2309,7 @@
public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
int prevState) {
// Since this is a binder call check if Bluetooth is on still
- if (mBluetoothState == BluetoothAdapter.STATE_OFF) return;
+ if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
if (!validateProfileConnectionState(state) ||
@@ -2405,6 +2325,10 @@
mAdapterConnectionState = state;
+ if (state == BluetoothProfile.STATE_DISCONNECTED) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED);
+ }
+
Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
@@ -2598,8 +2522,8 @@
/*package*/ native String getAdapterPathNative();
private native int isEnabledNative();
- private native int enableNative();
- private native int disableNative();
+ /*package*/ native int enableNative();
+ /*package*/ native int disableNative();
/*package*/ native Object[] getAdapterPropertiesNative();
private native Object[] getDevicePropertiesNative(String objectPath);
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 6ac99ca..270f512 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -36,7 +36,8 @@
*/
public abstract class SpellCheckerService extends Service {
private static final String TAG = SpellCheckerService.class.getSimpleName();
- public static final String SERVICE_INTERFACE = SpellCheckerService.class.getName();
+ public static final String SERVICE_INTERFACE =
+ "android.service.textservice.SpellCheckerService";
private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
diff --git a/core/java/android/service/textservice/SpellCheckerSession.java b/core/java/android/service/textservice/SpellCheckerSession.java
index a575220..400454d 100644
--- a/core/java/android/service/textservice/SpellCheckerSession.java
+++ b/core/java/android/service/textservice/SpellCheckerSession.java
@@ -25,10 +25,12 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.SuggestionsInfo;
import android.view.textservice.TextInfo;
import java.util.LinkedList;
+import java.util.Locale;
import java.util.Queue;
/**
@@ -42,6 +44,7 @@
private final InternalListener mInternalListener;
private final ITextServicesManager mTextServicesManager;
+ private final SpellCheckerInfo mSpellCheckerInfo;
private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
private boolean mIsUsed;
@@ -63,10 +66,12 @@
* Constructor
* @hide
*/
- public SpellCheckerSession(ITextServicesManager tsm, SpellCheckerSessionListener listener) {
- if (listener == null || tsm == null) {
+ public SpellCheckerSession(
+ SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) {
+ if (info == null || listener == null || tsm == null) {
throw new NullPointerException();
}
+ mSpellCheckerInfo = info;
mSpellCheckerSessionListenerImpl = new SpellCheckerSessionListenerImpl(mHandler);
mInternalListener = new InternalListener();
mTextServicesManager = tsm;
@@ -83,6 +88,14 @@
}
/**
+ * Get the spell checker service info this spell checker session has.
+ * @return SpellCheckerInfo for the specified locale.
+ */
+ public SpellCheckerInfo getSpellChecker() {
+ return mSpellCheckerInfo;
+ }
+
+ /**
* Finish this session and allow TextServicesManagerService to disconnect the bound spell
* checker.
*/
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index c913bb3..836867b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -161,6 +161,9 @@
*/
public static final int FLAGS_ORIENTATION_ANIMATION_DISABLE = 0x000000001;
+ // The mSurfaceControl will only be present for Surfaces used by the window
+ // server or system processes. When this class is parceled we defer to the
+ // mSurfaceControl to do the parceling. Otherwise we parcel the mNativeSurface.
@SuppressWarnings("unused")
private int mSurfaceControl;
@SuppressWarnings("unused")
@@ -202,6 +205,19 @@
native private static void nativeClassInit();
static { nativeClassInit(); }
+ /**
+ * Create Surface from a SurfaceTexture.
+ *
+ * @param surfaceTexture The {@link SurfaceTexture} that is updated by this Surface.
+ * @hide
+ */
+ public Surface(SurfaceTexture surfaceTexture) {
+ if (DEBUG_RELEASE) {
+ mCreationStack = new Exception();
+ }
+ mCanvas = new CompatibleCanvas();
+ initFromSurfaceTexture(surfaceTexture);
+ }
/**
* create a surface
@@ -505,5 +521,7 @@
private native void init(Parcel source);
+ private native void initFromSurfaceTexture(SurfaceTexture surfaceTexture);
+
private native int getIdentity();
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecb391d..dbb19e4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4280,6 +4280,36 @@
}
/**
+ * Set whether or not this view should account for system screen decorations
+ * such as the status bar and inset its content. This allows this view to be
+ * positioned in absolute screen coordinates and remain visible to the user.
+ *
+ * <p>This should only be used by top-level window decor views.
+ *
+ * @param fitSystemWindows true to inset content for system screen decorations, false for
+ * default behavior.
+ *
+ * @attr ref android.R.styleable#View_fitsSystemWindows
+ */
+ public void setFitsSystemWindows(boolean fitSystemWindows) {
+ setFlags(fitSystemWindows ? FITS_SYSTEM_WINDOWS : 0, FITS_SYSTEM_WINDOWS);
+ }
+
+ /**
+ * Check for the FITS_SYSTEM_WINDOWS flag. If this method returns true, this view
+ * will account for system screen decorations such as the status bar and inset its
+ * content. This allows the view to be positioned in absolute screen coordinates
+ * and remain visible to the user.
+ *
+ * @return true if this view will adjust its content bounds for system screen decorations.
+ *
+ * @attr ref android.R.styleable#View_fitsSystemWindows
+ */
+ public boolean fitsSystemWindows() {
+ return (mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS;
+ }
+
+ /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
@@ -12028,12 +12058,13 @@
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
- if (mLayoutParams != null && mParent != null) {
- mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
- }
-
- if (mParent != null && !mParent.isLayoutRequested()) {
- mParent.requestLayout();
+ if (mParent != null) {
+ if (mLayoutParams != null) {
+ mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
+ }
+ if (!mParent.isLayoutRequested()) {
+ mParent.requestLayout();
+ }
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 92a8ce7..6f90971 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2854,7 +2854,7 @@
// display lists to render, force an invalidate to allow the animation to
// continue drawing another frame
invalidate(true);
- if (a instanceof AlphaAnimation) {
+ if (a.hasAlpha()) {
// alpha animations should cause the child to recreate its display list
child.invalidate(true);
}
diff --git a/core/java/android/view/animation/AlphaAnimation.java b/core/java/android/view/animation/AlphaAnimation.java
index 651fe45..c4d9afc 100644
--- a/core/java/android/view/animation/AlphaAnimation.java
+++ b/core/java/android/view/animation/AlphaAnimation.java
@@ -78,4 +78,12 @@
public boolean willChangeBounds() {
return false;
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public boolean hasAlpha() {
+ return true;
+ }
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 87c759c..b7dfabc 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -1001,6 +1001,15 @@
}
/**
+ * Return true if this animation changes the view's alpha property.
+ *
+ * @hide
+ */
+ public boolean hasAlpha() {
+ return false;
+ }
+
+ /**
* Utility class to parse a string description of a size.
*/
protected static class Description {
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 873ce53..4f2542b 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -43,6 +43,8 @@
private static final int PROPERTY_CHANGE_BOUNDS_MASK = 0x80;
private int mFlags = 0;
+ private boolean mDirty;
+ private boolean mHasAlpha;
private ArrayList<Animation> mAnimations = new ArrayList<Animation>();
@@ -138,6 +140,28 @@
}
/**
+ * @hide
+ */
+ @Override
+ public boolean hasAlpha() {
+ if (mDirty) {
+ mDirty = mHasAlpha = false;
+
+ final int count = mAnimations.size();
+ final ArrayList<Animation> animations = mAnimations;
+
+ for (int i = 0; i < count; i++) {
+ if (animations.get(i).hasAlpha()) {
+ mHasAlpha = true;
+ break;
+ }
+ }
+ }
+
+ return mHasAlpha;
+ }
+
+ /**
* <p>Sets the duration of every child animation.</p>
*
* @param durationMillis the duration of the animation, in milliseconds, for
@@ -175,6 +199,8 @@
mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
mDuration = mLastEnd - mStartOffset;
}
+
+ mDirty = true;
}
/**
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 1205adf..d88a39f 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -18,8 +18,10 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -103,6 +105,24 @@
};
/**
+ * Load the user-displayed label for this spell checker.
+ *
+ * @param pm Supply a PackageManager used to load the spell checker's resources.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mService.loadLabel(pm);
+ }
+
+ /**
+ * Load the user-displayed icon for this spell checker.
+ *
+ * @param pm Supply a PackageManager used to load the spell checker's resources.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mService.loadIcon(pm);
+ }
+
+ /**
* Used to make this class parcelable.
*/
@Override
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index b0ccbea..3332f1e 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -23,27 +23,23 @@
* This class contains a metadata of suggestions from the text service
*/
public final class SuggestionsInfo implements Parcelable {
+ private static final String[] EMPTY = new String[0];
+
/**
* Flag of the attributes of the suggestions that can be obtained by
* {@link #getSuggestionsAttributes}: this tells that the requested word was found
* in the dictionary in the text service.
*/
public static final int RESULT_ATTR_IN_THE_DICTIONARY = 0x0001;
- /** Flag of the attributes of the suggestions that can be obtained by
- * {@link #getSuggestionsAttributes}: this tells that there are one or more suggestions
- * available for the requested word. This doesn't necessarily mean that the suggestions
- * are actually in this SuggestionsInfo. For instance, the caller could have been asked to
- * limit the maximum number of suggestions returned.
- */
- public static final int RESULT_ATTR_SUGGESTIONS_AVAILABLE = 0x0002;
/**
* Flag of the attributes of the suggestions that can be obtained by
* {@link #getSuggestionsAttributes}: this tells that the text service thinks the requested
* word looks a typo.
*/
- public static final int RESULT_ATTR_LOOKS_TYPO = 0x0004;
+ public static final int RESULT_ATTR_LOOKS_TYPO = 0x0002;
private final int mSuggestionsAttributes;
private final String[] mSuggestions;
+ private final boolean mSuggestionsAvailable;
private int mCookie;
private int mSequence;
@@ -53,11 +49,14 @@
* @param suggestions from the text service
*/
public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) {
- if (suggestions == null) {
- throw new NullPointerException();
- }
mSuggestionsAttributes = suggestionsAttributes;
- mSuggestions = suggestions;
+ if (suggestions == null) {
+ mSuggestions = EMPTY;
+ mSuggestionsAvailable = false;
+ } else {
+ mSuggestions = suggestions;
+ mSuggestionsAvailable = true;
+ }
mCookie = 0;
mSequence = 0;
}
@@ -72,10 +71,13 @@
public SuggestionsInfo(
int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
if (suggestions == null) {
- throw new NullPointerException();
+ mSuggestions = EMPTY;
+ mSuggestionsAvailable = false;
+ } else {
+ mSuggestions = suggestions;
+ mSuggestionsAvailable = true;
}
mSuggestionsAttributes = suggestionsAttributes;
- mSuggestions = suggestions;
mCookie = cookie;
mSequence = sequence;
}
@@ -85,6 +87,7 @@
mSuggestions = source.readStringArray();
mCookie = source.readInt();
mSequence = source.readInt();
+ mSuggestionsAvailable = source.readInt() == 1;
}
/**
@@ -99,6 +102,7 @@
dest.writeStringArray(mSuggestions);
dest.writeInt(mCookie);
dest.writeInt(mSequence);
+ dest.writeInt(mSuggestionsAvailable ? 1 : 0);
}
/**
@@ -136,9 +140,15 @@
}
/**
- * @return the count of suggestions
+ * @return the count of the suggestions. If there's no suggestions at all, this method returns
+ * -1. Even if this method returns 0, it doesn't necessarily mean that there are no suggestions
+ * for the requested word. For instance, the caller could have been asked to limit the maximum
+ * number of suggestions returned.
*/
public int getSuggestionsCount() {
+ if (!mSuggestionsAvailable) {
+ return -1;
+ }
return mSuggestions.length;
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 6fa7e4d..229b414 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -63,37 +63,31 @@
return sInstance;
}
-
/**
- * Get the current spell checker service info for the specified locale.
- * @param locale locale of a spell checker
- * @return SpellCheckerInfo for the specified locale.
+ * Get a spell checker session for the specified spell checker
+ * @param locale the locale for the spell checker
+ * @param listener a spell checker session lister for getting results from a spell checker.
+ * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
+ * languages in settings will be returned.
+ * @return the spell checker session of the spell checker
*/
// TODO: Add a method to get enabled spell checkers.
- public SpellCheckerInfo getCurrentSpellChecker(Locale locale) {
- if (locale == null) {
- throw new NullPointerException("locale is null");
+ // TODO: Handle referToSpellCheckerLanguageSettings
+ public SpellCheckerSession newSpellCheckerSession(Locale locale,
+ SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
+ if (locale == null || listener == null) {
+ throw new NullPointerException();
}
+ final SpellCheckerInfo info;
try {
- return sService.getCurrentSpellChecker(locale.toString());
+ info = sService.getCurrentSpellChecker(locale.toString());
} catch (RemoteException e) {
return null;
}
- }
-
- /**
- * Get a spell checker session for a specified spell checker
- * @param info SpellCheckerInfo of the spell checker
- * @param locale the locale for the spell checker
- * @param listener a spell checker session lister for getting results from a spell checker.
- * @return the spell checker session of the spell checker
- */
- public SpellCheckerSession newSpellCheckerSession(
- SpellCheckerInfo info, Locale locale, SpellCheckerSessionListener listener) {
- if (info == null || locale == null || listener == null) {
- throw new NullPointerException();
+ if (info == null) {
+ return null;
}
- final SpellCheckerSession session = new SpellCheckerSession(sService, listener);
+ final SpellCheckerSession session = new SpellCheckerSession(info, sService, listener);
try {
sService.getSpellCheckerService(
info, locale.toString(), session.getTextServicesSessionListener(),
@@ -103,4 +97,27 @@
}
return session;
}
+
+ /**
+ * @hide
+ */
+ public SpellCheckerInfo[] getEnabledSpellCheckers() {
+ try {
+ return sService.getEnabledSpellCheckers();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public SpellCheckerInfo getCurrentSpellChecker() {
+ try {
+ // Passing null as a locale for ICS
+ return sService.getCurrentSpellChecker(null);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
}
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index bb4d192..620973e 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.Uri;
+import android.provider.Settings;
import android.util.Log;
import java.io.InputStream;
@@ -38,7 +39,7 @@
private static boolean initialized = false;
- private static void checkIntialized() {
+ private static void checkInitialized() {
if (!initialized) {
throw new IllegalStateException("Call CookieSyncManager::createInstance() or create a webview before using this class");
}
@@ -63,7 +64,7 @@
* @return String The application's database directory
*/
private static synchronized String getDatabaseDirectory() {
- checkIntialized();
+ checkInitialized();
if (sDatabaseDirectory == null)
sDatabaseDirectory = sContext.getDatabasePath("dummy").getParent();
@@ -76,7 +77,7 @@
* @return String The application's cache directory
*/
private static synchronized String getCacheDirectory() {
- checkIntialized();
+ checkInitialized();
if (sCacheDirectory == null)
sCacheDirectory = sContext.getCacheDir().getAbsolutePath();
@@ -166,5 +167,13 @@
return sUseChromiumHttpStack;
}
+ private static synchronized String getAutofillQueryUrl() {
+ checkInitialized();
+ // If the device has not checked in it won't have pulled down the system setting for the
+ // Autofill Url. In that case we will not make autofill server requests.
+ return Settings.Secure.getString(sContext.getContentResolver(),
+ Settings.Secure.WEB_AUTOFILL_QUERY_URL);
+ }
+
private static native boolean nativeUseChromiumHttpStack();
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6a3b2ff..e24ab58 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4701,6 +4701,7 @@
private Message mUpdateMessage;
private boolean mAutoFillable;
private boolean mAutoComplete;
+ private WebSettings mWebSettings;
public RequestFormData(String name, String url, Message msg,
boolean autoFillable, boolean autoComplete) {
@@ -4709,6 +4710,7 @@
mUpdateMessage = msg;
mAutoFillable = autoFillable;
mAutoComplete = autoComplete;
+ mWebSettings = getSettings();
}
public void run() {
@@ -4718,8 +4720,7 @@
// Note that code inside the adapter click handler in WebTextView depends
// on the AutoFill item being at the top of the drop down list. If you change
// the order, make sure to do it there too!
- WebSettings settings = getSettings();
- if (settings != null && settings.getAutoFillProfile() != null) {
+ if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
pastEntries.add(getResources().getText(
com.android.internal.R.string.autofill_this_form).toString() +
" " +
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 563fc26..36927ca 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -91,6 +91,7 @@
private boolean mLayoutInScreen;
private boolean mClipToScreen;
private boolean mAllowScrollingAnchorParent = true;
+ private boolean mLayoutInsetDecor = false;
private OnTouchListener mTouchInterceptor;
@@ -658,6 +659,22 @@
}
/**
+ * Allows the popup window to force the flag
+ * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}, overriding default behavior.
+ * This will cause the popup to inset its content to account for system windows overlaying
+ * the screen, such as the status bar.
+ *
+ * <p>This will often be combined with {@link #setLayoutInScreenEnabled(boolean)}.
+ *
+ * @param enabled true if the popup's views should inset content to account for system windows,
+ * the way that decor views behave for full-screen windows.
+ * @hide
+ */
+ public void setLayoutInsetDecor(boolean enabled) {
+ mLayoutInsetDecor = enabled;
+ }
+
+ /**
* Set the layout type for this window. Should be one of the TYPE constants defined in
* {@link WindowManager.LayoutParams}.
*
@@ -942,6 +959,7 @@
if (mContext != null) {
p.packageName = mContext.getPackageName();
}
+ mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
mWindowManager.addView(mPopupView, p);
}
@@ -1012,6 +1030,9 @@
if (mLayoutInScreen) {
curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
}
+ if (mLayoutInsetDecor) {
+ curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+ }
return curFlags;
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index b7565f3..0c80a11 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -34,6 +34,7 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.R;
@@ -360,6 +361,13 @@
}
}
+ @Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
+ Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
+ event.getText().add(switchText.getText());
+ }
+
private Layout makeLayout(CharSequence text) {
return new StaticLayout(text, mTextPaint,
(int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index ad0c1ff..2a045e3 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -32,4 +32,5 @@
in ITextServicesSessionListener tsListener,
in ISpellCheckerSessionListener scListener);
oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
+ SpellCheckerInfo[] getEnabledSpellCheckers();
}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 69b80d9..9ecd29f 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -41,6 +41,9 @@
/** Non system protocols */
public static final int BASE_WIFI = 0x00020000;
public static final int BASE_WIFI_WATCHDOG = 0x00021000;
+ public static final int BASE_WIFI_P2P_MANAGER = 0x00022000;
+ public static final int BASE_WIFI_P2P_SERVICE = 0x00023000;
+ public static final int BASE_WIFI_MONITOR = 0x00024000;
public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 8b74f3d..09262e0 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -286,8 +286,11 @@
public void setSplitActionBar(boolean splitActionBar) {
if (mSplitActionBar != splitActionBar) {
if (mMenuView != null) {
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) {
+ oldParent.removeView(mMenuView);
+ }
if (splitActionBar) {
- removeView(mMenuView);
if (mSplitView != null) {
mSplitView.addView(mMenuView);
}
@@ -333,7 +336,10 @@
MenuBuilder builder = (MenuBuilder) menu;
mOptionsMenu = builder;
if (mMenuView != null) {
- removeView(mMenuView);
+ final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
+ if (oldParent != null) {
+ oldParent.removeView(mMenuView);
+ }
}
if (mActionMenuPresenter == null) {
mActionMenuPresenter = new ActionMenuPresenter();
@@ -352,6 +358,10 @@
builder.addMenuPresenter(mActionMenuPresenter);
builder.addMenuPresenter(mExpandedMenuPresenter);
menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ final ViewGroup oldParent = (ViewGroup) menuView.getParent();
+ if (oldParent != null && oldParent != this) {
+ oldParent.removeView(menuView);
+ }
addView(menuView, layoutParams);
} else {
mActionMenuPresenter.setExpandedActionViewsExclusive(false);
@@ -366,6 +376,10 @@
builder.addMenuPresenter(mExpandedMenuPresenter);
menuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
if (mSplitView != null) {
+ final ViewGroup oldParent = (ViewGroup) menuView.getParent();
+ if (oldParent != null && oldParent != mSplitView) {
+ oldParent.removeView(menuView);
+ }
mSplitView.addView(menuView, layoutParams);
} else {
// We'll add this later if we missed it this time.
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 0c81634..3cbd912 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -28,6 +28,9 @@
#define WIFI_PKG_NAME "android/net/wifi/WifiNative"
#define BUF_SIZE 256
+//TODO: This file can be refactored to push a lot of the functionality to java
+//with just a few JNI calls - doBoolean/doInt/doString
+
namespace android {
static jboolean sScanModeActive = false;
@@ -537,6 +540,35 @@
}
+static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jobject, jstring javaCommand)
+{
+ ScopedUtfChars command(env, javaCommand);
+ if (command.c_str() == NULL) {
+ return JNI_FALSE;
+ }
+ return doBooleanCommand("OK", "%s", command.c_str());
+}
+
+static jint android_net_wifi_doIntCommand(JNIEnv* env, jobject, jstring javaCommand)
+{
+ ScopedUtfChars command(env, javaCommand);
+ if (command.c_str() == NULL) {
+ return -1;
+ }
+ return doIntCommand("%s", command.c_str());
+}
+
+static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring javaCommand)
+{
+ ScopedUtfChars command(env, javaCommand);
+ if (command.c_str() == NULL) {
+ return NULL;
+ }
+ return doStringCommand(env, "%s", command.c_str());
+}
+
+
+
// ----------------------------------------------------------------------------
/*
@@ -608,6 +640,9 @@
(void*) android_net_wifi_setCountryCodeCommand},
{ "enableBackgroundScanCommand", "(Z)V", (void*) android_net_wifi_enableBackgroundScanCommand},
{ "setScanIntervalCommand", "(I)V", (void*) android_net_wifi_setScanIntervalCommand},
+ { "doBooleanCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_doBooleanCommand},
+ { "doIntCommand", "(Ljava/lang/String;)I", (void*) android_net_wifi_doIntCommand},
+ { "doStringCommand", "(Ljava/lang/String;)Ljava/lang/String;", (void*) android_net_wifi_doStringCommand},
};
int register_android_net_wifi_WifiManager(JNIEnv* env)
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 0dc9293..4c1ca31 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -22,6 +22,7 @@
#include "android/graphics/GraphicsJNI.h"
#include <binder/IMemory.h>
+#include <gui/SurfaceTexture.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <surfaceflinger/Surface.h>
#include <ui/Region.h>
@@ -38,6 +39,7 @@
#include "JNIHelp.h"
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <utils/misc.h>
@@ -244,6 +246,19 @@
setSurfaceControl(env, clazz, surface);
}
+static void Surface_initFromSurfaceTexture(
+ JNIEnv* env, jobject clazz, jobject jst)
+{
+ sp<ISurfaceTexture> st(SurfaceTexture_getSurfaceTexture(env, jst));
+ sp<Surface> surface(new Surface(st));
+ if (surface == NULL) {
+ jniThrowException(env, OutOfResourcesException, NULL);
+ return;
+ }
+ setSurfaceControl(env, clazz, NULL);
+ setSurface(env, clazz, surface);
+}
+
static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
{
Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);
@@ -761,10 +776,26 @@
return;
}
+ // The Java instance may have a SurfaceControl (in the case of the
+ // WindowManager or a system app). In that case, we defer to the
+ // SurfaceControl to send its ISurface. Otherwise, if the Surface is
+ // available we let it parcel itself. Finally, if the Surface is also
+ // NULL we fall back to using the SurfaceControl path which sends an
+ // empty surface; this matches legacy behavior.
const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
- SurfaceControl::writeSurfaceToParcel(control, parcel);
+ if (control != NULL) {
+ SurfaceControl::writeSurfaceToParcel(control, parcel);
+ } else {
+ sp<Surface> surface(Surface_getSurface(env, clazz));
+ if (surface != NULL) {
+ Surface::writeToParcel(surface, parcel);
+ } else {
+ SurfaceControl::writeSurfaceToParcel(NULL, parcel);
+ }
+ }
if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
- setSurfaceControl(env, clazz, 0);
+ setSurfaceControl(env, clazz, NULL);
+ setSurface(env, clazz, NULL);
}
}
@@ -784,6 +815,7 @@
{"nativeClassInit", "()V", (void*)nativeClassInit },
{"init", "(Landroid/view/SurfaceSession;ILjava/lang/String;IIIII)V", (void*)Surface_init },
{"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel },
+ {"initFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)V", (void*)Surface_initFromSurfaceTexture },
{"getIdentity", "()I", (void*)Surface_getIdentity },
{"destroy", "()V", (void*)Surface_destroy },
{"release", "()V", (void*)Surface_release },
diff --git a/core/res/res/layout/wifi_p2p_go_negotiation_request_alert.xml b/core/res/res/layout/wifi_p2p_go_negotiation_request_alert.xml
new file mode 100644
index 0000000..41ca657
--- /dev/null
+++ b/core/res/res/layout/wifi_p2p_go_negotiation_request_alert.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <EditText android:id="@+id/wifi_p2p_wps_pin"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ />
+</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 215700c..65dce49 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -182,6 +182,9 @@
<!-- Boolean indicating whether the wifi chipset has dual frequency band support -->
<bool translatable="false" name="config_wifi_dual_band_support">false</bool>
+ <!-- Boolean indicating whether the wifi chipset has p2p support -->
+ <bool translatable="false" name="config_wifi_p2p_support">false</bool>
+
<!-- Boolean indicating whether the wifi chipset supports background scanning mechanism.
This mechanism allows the host to remain in suspend state and the dongle to actively
scan and wake the host when a configured SSID is detected by the dongle. This chipset
@@ -466,6 +469,10 @@
speech -->
<bool name="config_bluetooth_wide_band_speech">true</bool>
+ <!-- Boolean indicating if current platform supports quick switch-on/off of
+ Bluetooth Module -->
+ <bool name="config_bluetooth_adapter_quick_switch">true</bool>
+
<!-- The default data-use polling period. -->
<integer name="config_datause_polling_period_sec">600</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index feac38d..e1a31f4 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2583,6 +2583,14 @@
<!-- Do not translate. Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
+ <!-- Wi-Fi p2p dialog title-->
+ <string name="wifi_p2p_dialog_title">Wi-Fi Direct</string>
+ <string name="wifi_p2p_turnon_message">Start Wi-Fi Direct operation. This will turn off Wi-Fi client/hotspot operation.</string>
+ <string name="wifi_p2p_failed_message">Failed to start Wi-Fi Direct</string>
+ <string name="wifi_p2p_pbc_go_negotiation_request_message">Wi-Fi Direct connection setup request from <xliff:g id="p2p_device_address">%1$s</xliff:g>. Click OK to accept. </string>
+ <string name="wifi_p2p_pin_go_negotiation_request_message">Wi-Fi Direct connection setup request from <xliff:g id="p2p_device_address">%1$s</xliff:g>. Enter pin to proceed. </string>
+ <string name="wifi_p2p_pin_display_message">WPS pin <xliff:g id="p2p_wps_pin">%1$s</xliff:g> needs to be entered on the peer device <xliff:g id="p2p_client_address">%2$s</xliff:g> for connection setup to proceed </string>
+
<!-- Name of the dialog that lets the user choose an accented character to insert -->
<string name="select_character">Insert character</string>
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 405a25a..1eda646 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -51,7 +51,7 @@
// the given slot index, and the client is expected to mirror the
// slot->buffer mapping so that it's not necessary to transfer a
// GraphicBuffer for every dequeue operation.
- virtual sp<GraphicBuffer> requestBuffer(int slot) = 0;
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;
// setBufferCount sets the number of buffer slots available. Calling this
// will also cause all buffer slots to be emptied. The caller should empty
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 5c40419..2a8e725 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -69,7 +69,7 @@
// SurfaceTexture object (i.e. they are not owned by the client).
virtual status_t setBufferCount(int bufferCount);
- virtual sp<GraphicBuffer> requestBuffer(int buf);
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
// dequeueBuffer gets the next buffer slot index for the client to use. If a
// buffer slot is available then that slot index is written to the location
@@ -190,6 +190,17 @@
// getCurrentScalingMode returns the scaling mode of the current buffer
uint32_t getCurrentScalingMode() const;
+ // abandon frees all the buffers and puts the SurfaceTexture into the
+ // 'abandoned' state. Once put in this state the SurfaceTexture can never
+ // leave it. When in the 'abandoned' state, all methods of the
+ // ISurfaceTexture interface will fail with the NO_INIT error.
+ //
+ // Note that while calling this method causes all the buffers to be freed
+ // from the perspective of the the SurfaceTexture, if there are additional
+ // references on the buffers (e.g. if a buffer is referenced by a client or
+ // by OpenGL ES as a texture) then those buffer will remain allocated.
+ void abandon();
+
// dump our state in a String
void dump(String8& result) const;
void dump(String8& result, const char* prefix, char* buffer, size_t SIZE) const;
@@ -406,6 +417,13 @@
typedef Vector<int> Fifo;
Fifo mQueue;
+ // mAbandoned indicates that the SurfaceTexture will no longer be used to
+ // consume images buffers pushed to it using the ISurfaceTexture interface.
+ // It is initialized to false, and set to true in the abandon method. A
+ // SurfaceTexture that has been abandoned will return the NO_INIT error from
+ // all ISurfaceTexture methods capable of returning an error.
+ bool mAbandoned;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index a73267d..007aea6 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -26,6 +26,7 @@
class ICamera;
class ICameraRecordingProxy;
class IMediaRecorderClient;
+class ISurfaceTexture;
class IMediaRecorder: public IInterface
{
@@ -55,6 +56,7 @@
virtual status_t init() = 0;
virtual status_t close() = 0;
virtual status_t release() = 0;
+ virtual sp<ISurfaceTexture> querySurfaceMediaSource() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 1c08969..ef799f5 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -26,6 +26,7 @@
class ICameraRecordingProxy;
class Surface;
+class ISurfaceTexture;
struct MediaRecorderBase {
MediaRecorderBase() {}
@@ -54,6 +55,7 @@
virtual status_t reset() = 0;
virtual status_t getMaxAmplitude(int *max) = 0;
virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
+ virtual sp<ISurfaceTexture> querySurfaceMediaSource() const = 0;
private:
MediaRecorderBase(const MediaRecorderBase &);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index af12d3c..72d3736 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -31,12 +31,15 @@
class IMediaRecorder;
class ICamera;
class ICameraRecordingProxy;
+class ISurfaceTexture;
+class SurfaceTextureClient;
typedef void (*media_completion_f)(status_t status, void *cookie);
enum video_source {
VIDEO_SOURCE_DEFAULT = 0,
VIDEO_SOURCE_CAMERA = 1,
+ VIDEO_SOURCE_GRALLOC_BUFFER = 2,
VIDEO_SOURCE_LIST_END // must be last - used to validate audio source type
};
@@ -226,6 +229,7 @@
status_t close();
status_t release();
void notify(int msg, int ext1, int ext2);
+ sp<ISurfaceTexture> querySurfaceMediaSourceFromMediaServer();
private:
void doCleanUp();
@@ -233,6 +237,12 @@
sp<IMediaRecorder> mMediaRecorder;
sp<MediaRecorderListener> mListener;
+
+ // Reference toISurfaceTexture
+ // for encoding GL Frames. That is useful only when the
+ // video source is set to VIDEO_SOURCE_GRALLOC_BUFFER
+ sp<ISurfaceTexture> mSurfaceMediaSource;
+
media_recorder_states mCurrentState;
bool mIsAudioSourceSet;
bool mIsVideoSourceSet;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 20fcde5..2932744 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -329,6 +329,7 @@
void restorePatchedDataPointer(BufferInfo *info);
status_t applyRotation();
+ status_t waitForBufferFilled_l();
int64_t retrieveDecodingTimeUs(bool isCodecSpecific);
@@ -348,6 +349,8 @@
// that encode content of the given type.
// profile and level indications only make sense for h.263, mpeg4 and avc
// video.
+// If hwCodecOnly==true, only returns hardware-based components, software and
+// hardware otherwise.
// The profile/level values correspond to
// OMX_VIDEO_H263PROFILETYPE, OMX_VIDEO_MPEG4PROFILETYPE,
// OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263LEVELTYPE, OMX_VIDEO_MPEG4LEVELTYPE
@@ -358,6 +361,11 @@
const char *mimeType, bool queryDecoders, bool hwCodecOnly,
Vector<CodecCapabilities> *results);
+status_t QueryCodecs(
+ const sp<IOMX> &omx,
+ const char *mimeType, bool queryDecoders,
+ Vector<CodecCapabilities> *results);
+
} // namespace android
#endif // OMX_CODEC_H_
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index e1852ec..56bd9c3 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -64,8 +64,8 @@
virtual sp<MetaData> getFormat();
// Get / Set the frame rate used for encoding. Default fps = 30
- void setFrameRate(uint32_t fps) ;
- uint32_t getFrameRate( ) const;
+ status_t setFrameRate(int32_t fps) ;
+ int32_t getFrameRate( ) const;
// The call for the StageFrightRecorder to tell us that
// it is done using the MediaBuffer data so that its state
@@ -81,7 +81,7 @@
// SurfaceMediaSource object (i.e. they are not owned by the client).
virtual status_t setBufferCount(int bufferCount);
- virtual sp<GraphicBuffer> requestBuffer(int buf);
+ virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
// dequeueBuffer gets the next buffer slot index for the client to use. If a
// buffer slot is available then that slot index is written to the location
@@ -163,13 +163,6 @@
// when a new frame becomes available.
void setFrameAvailableListener(const sp<FrameAvailableListener>& listener);
- // getAllocator retrieves the binder object that must be referenced as long
- // as the GraphicBuffers dequeued from this SurfaceMediaSource are referenced.
- // Holding this binder reference prevents SurfaceFlinger from freeing the
- // buffers before the client is done with them.
- sp<IBinder> getAllocator();
-
-
// getCurrentBuffer returns the buffer associated with the current image.
sp<GraphicBuffer> getCurrentBuffer() const;
@@ -178,7 +171,11 @@
void dump(String8& result, const char* prefix, char* buffer,
size_t SIZE) const;
- protected:
+ // isMetaDataStoredInVideoBuffers tells the encoder whether we will
+ // pass metadata through the buffers. Currently, it is force set to true
+ bool isMetaDataStoredInVideoBuffers() const;
+
+protected:
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
// all slots.
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 9c352ad..0460bbd 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -122,7 +122,10 @@
uint32_t reserved[2];
};
+ explicit Surface(const sp<ISurfaceTexture>& st);
+
static status_t writeToParcel(const sp<Surface>& control, Parcel* parcel);
+
static sp<Surface> readFromParcel(const Parcel& data);
static bool isValid(const sp<Surface>& surface) {
return (surface != 0) && surface->isValid();
@@ -147,14 +150,14 @@
Surface& operator = (Surface& rhs);
Surface(const Surface& rhs);
- Surface(const sp<SurfaceControl>& control);
+ explicit Surface(const sp<SurfaceControl>& control);
Surface(const Parcel& data, const sp<IBinder>& ref);
~Surface();
/*
* private stuff...
*/
- void init();
+ void init(const sp<ISurfaceTexture>& surfaceTexture);
static void cleanCachedSurfacesLocked();
diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp
index c9c7397..55246dc 100644
--- a/libs/gui/ISurfaceTexture.cpp
+++ b/libs/gui/ISurfaceTexture.cpp
@@ -54,18 +54,18 @@
{
}
- virtual sp<GraphicBuffer> requestBuffer(int bufferIdx) {
+ virtual status_t requestBuffer(int bufferIdx, sp<GraphicBuffer>* buf) {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor());
data.writeInt32(bufferIdx);
remote()->transact(REQUEST_BUFFER, data, &reply);
- sp<GraphicBuffer> buffer;
bool nonNull = reply.readInt32();
if (nonNull) {
- buffer = new GraphicBuffer();
- reply.read(*buffer);
+ *buf = new GraphicBuffer();
+ reply.read(**buf);
}
- return buffer;
+ status_t result = reply.readInt32();
+ return result;
}
virtual status_t setBufferCount(int bufferCount)
@@ -192,11 +192,13 @@
case REQUEST_BUFFER: {
CHECK_INTERFACE(ISurfaceTexture, data, reply);
int bufferIdx = data.readInt32();
- sp<GraphicBuffer> buffer(requestBuffer(bufferIdx));
+ sp<GraphicBuffer> buffer;
+ int result = requestBuffer(bufferIdx, &buffer);
reply->writeInt32(buffer != 0);
if (buffer != 0) {
reply->write(*buffer);
}
+ reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_BUFFER_COUNT: {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c4f9e53..ccf98e5 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -184,6 +184,7 @@
identity = control->mIdentity;
}
parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+ parcel->writeStrongBinder(NULL); // NULL ISurfaceTexture in this case.
parcel->writeInt32(identity);
return NO_ERROR;
}
@@ -192,7 +193,8 @@
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == 0) {
- mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));
+ sp<SurfaceControl> surface_control(const_cast<SurfaceControl*>(this));
+ mSurfaceData = new Surface(surface_control);
}
return mSurfaceData;
}
@@ -208,31 +210,58 @@
mSurface(surface->mSurface),
mIdentity(surface->mIdentity)
{
- init();
+ sp<ISurfaceTexture> st;
+ if (mSurface != NULL) {
+ st = mSurface->getSurfaceTexture();
+ }
+ init(st);
}
Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)
: SurfaceTextureClient()
{
- mSurface = interface_cast<ISurface>(ref);
+ mSurface = interface_cast<ISurface>(ref);
+ sp<IBinder> st_binder(parcel.readStrongBinder());
+ sp<ISurfaceTexture> st;
+ if (st_binder != NULL) {
+ st = interface_cast<ISurfaceTexture>(st_binder);
+ } else if (mSurface != NULL) {
+ st = mSurface->getSurfaceTexture();
+ }
+
mIdentity = parcel.readInt32();
- init();
+ init(st);
+}
+
+Surface::Surface(const sp<ISurfaceTexture>& st)
+ : SurfaceTextureClient(),
+ mSurface(NULL),
+ mIdentity(0)
+{
+ init(st);
}
status_t Surface::writeToParcel(
const sp<Surface>& surface, Parcel* parcel)
{
sp<ISurface> sur;
+ sp<ISurfaceTexture> st;
uint32_t identity = 0;
if (Surface::isValid(surface)) {
sur = surface->mSurface;
+ st = surface->getISurfaceTexture();
identity = surface->mIdentity;
- } else if (surface != 0 && surface->mSurface != 0) {
- LOGW("Parceling invalid surface with non-NULL ISurface as NULL: "
- "mSurface = %p, mIdentity = %d",
- surface->mSurface.get(), surface->mIdentity);
+ } else if (surface != 0 &&
+ (surface->mSurface != NULL ||
+ surface->getISurfaceTexture() != NULL)) {
+ LOGE("Parceling invalid surface with non-NULL ISurface/ISurfaceTexture as NULL: "
+ "mSurface = %p, surfaceTexture = %p, mIdentity = %d, ",
+ surface->mSurface.get(), surface->getISurfaceTexture().get(),
+ surface->mIdentity);
}
- parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+
+ parcel->writeStrongBinder(sur != NULL ? sur->asBinder() : NULL);
+ parcel->writeStrongBinder(st != NULL ? st->asBinder() : NULL);
parcel->writeInt32(identity);
return NO_ERROR;
@@ -249,8 +278,8 @@
surface = new Surface(data, binder);
sCachedSurfaces.add(binder, surface);
}
- if (surface->mSurface == 0) {
- surface = 0;
+ if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
+ surface = 0;
}
cleanCachedSurfacesLocked();
return surface;
@@ -267,10 +296,9 @@
}
}
-void Surface::init()
+void Surface::init(const sp<ISurfaceTexture>& surfaceTexture)
{
- if (mSurface != NULL) {
- sp<ISurfaceTexture> surfaceTexture(mSurface->getSurfaceTexture());
+ if (mSurface != NULL || surfaceTexture != NULL) {
LOGE_IF(surfaceTexture==0, "got a NULL ISurfaceTexture from ISurface");
if (surfaceTexture != NULL) {
setISurfaceTexture(surfaceTexture);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index f04f5c2..8d19957 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -93,7 +93,8 @@
mTexName(tex),
mSynchronousMode(false),
mAllowSynchronousMode(allowSynchronousMode),
- mConnectedApi(NO_CONNECTED_API) {
+ mConnectedApi(NO_CONNECTED_API),
+ mAbandoned(false) {
LOGV("SurfaceTexture::SurfaceTexture");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -149,6 +150,11 @@
LOGV("SurfaceTexture::setBufferCount");
Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ LOGE("setBufferCount: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
if (bufferCount > NUM_BUFFER_SLOTS) {
LOGE("setBufferCount: bufferCount larger than slots available");
return BAD_VALUE;
@@ -198,22 +204,32 @@
return OK;
}
-sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf) {
+status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
LOGV("SurfaceTexture::requestBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
- LOGE("requestBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, buf);
- return 0;
+ if (mAbandoned) {
+ LOGE("requestBuffer: SurfaceTexture has been abandoned!");
+ return NO_INIT;
}
- mSlots[buf].mRequestBufferCalled = true;
- return mSlots[buf].mGraphicBuffer;
+ if (slot < 0 || mBufferCount <= slot) {
+ LOGE("requestBuffer: slot index out of range [0, %d]: %d",
+ mBufferCount, slot);
+ return BAD_VALUE;
+ }
+ mSlots[slot].mRequestBufferCalled = true;
+ *buf = mSlots[slot].mGraphicBuffer;
+ return NO_ERROR;
}
status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) {
LOGV("SurfaceTexture::dequeueBuffer");
+ if (mAbandoned) {
+ LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
if ((w && !h) || (!w && h)) {
LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
return BAD_VALUE;
@@ -251,6 +267,11 @@
// wait for the FIFO to drain
while (!mQueue.isEmpty()) {
mDequeueCondition.wait(mMutex);
+ if (mAbandoned) {
+ LOGE("dequeueBuffer: SurfaceTexture was abandoned while "
+ "blocked!");
+ return NO_INIT;
+ }
}
minBufferCountNeeded = mSynchronousMode ?
MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS;
@@ -379,6 +400,11 @@
status_t SurfaceTexture::setSynchronousMode(bool enabled) {
Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ LOGE("setSynchronousMode: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
status_t err = OK;
if (!mAllowSynchronousMode && enabled)
return err;
@@ -409,6 +435,10 @@
{ // scope for the lock
Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ LOGE("queueBuffer: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
if (buf < 0 || buf >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf);
@@ -474,6 +504,12 @@
void SurfaceTexture::cancelBuffer(int buf) {
LOGV("SurfaceTexture::cancelBuffer");
Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ LOGW("cancelBuffer: SurfaceTexture has been abandoned!");
+ return;
+ }
+
if (buf < 0 || buf >= mBufferCount) {
LOGE("cancelBuffer: slot index out of range [0, %d]: %d",
mBufferCount, buf);
@@ -490,6 +526,10 @@
status_t SurfaceTexture::setCrop(const Rect& crop) {
LOGV("SurfaceTexture::setCrop");
Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ LOGE("setCrop: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
mNextCrop = crop;
return OK;
}
@@ -497,6 +537,10 @@
status_t SurfaceTexture::setTransform(uint32_t transform) {
LOGV("SurfaceTexture::setTransform");
Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ LOGE("setTransform: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
mNextTransform = transform;
return OK;
}
@@ -504,6 +548,12 @@
status_t SurfaceTexture::connect(int api) {
LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ LOGE("connect: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
int err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
@@ -528,6 +578,12 @@
status_t SurfaceTexture::disconnect(int api) {
LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ LOGE("connect: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
int err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
@@ -819,6 +875,12 @@
int SurfaceTexture::query(int what, int* outValue)
{
Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ LOGE("query: SurfaceTexture has been abandoned!");
+ return NO_INIT;
+ }
+
int value;
switch (what) {
case NATIVE_WINDOW_WIDTH:
@@ -845,6 +907,13 @@
return NO_ERROR;
}
+void SurfaceTexture::abandon() {
+ Mutex::Autolock lock(mMutex);
+ freeAllBuffers();
+ mAbandoned = true;
+ mDequeueCondition.signal();
+}
+
void SurfaceTexture::dump(String8& result) const
{
char buffer[1024];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 688b99b..df0ad5a 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -148,10 +148,11 @@
}
if ((result & ISurfaceTexture::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) {
- gbuf = mSurfaceTexture->requestBuffer(buf);
- if (gbuf == 0) {
- LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
- return NO_MEMORY;
+ result = mSurfaceTexture->requestBuffer(buf, &gbuf);
+ if (result != NO_ERROR) {
+ LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed: %d",
+ result);
+ return result;
}
mQueryWidth = gbuf->width;
mQueryHeight = gbuf->height;
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 964c483..44babcf 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -1014,6 +1014,83 @@
EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
}
+TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
+ class ProducerThread : public Thread {
+ public:
+ ProducerThread(const sp<ANativeWindow>& anw):
+ mANW(anw),
+ mDequeueError(NO_ERROR) {
+ }
+
+ virtual ~ProducerThread() {
+ }
+
+ virtual bool threadLoop() {
+ Mutex::Autolock lock(mMutex);
+ ANativeWindowBuffer* anb;
+
+ // Frame 1
+ if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+ return false;
+ }
+ if (anb == NULL) {
+ return false;
+ }
+ if (mANW->queueBuffer(mANW.get(), anb)
+ != NO_ERROR) {
+ return false;
+ }
+
+ // Frame 2
+ if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+ return false;
+ }
+ if (anb == NULL) {
+ return false;
+ }
+ if (mANW->queueBuffer(mANW.get(), anb)
+ != NO_ERROR) {
+ return false;
+ }
+
+ // Frame 3 - error expected
+ mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb);
+ return false;
+ }
+
+ status_t getDequeueError() {
+ Mutex::Autolock lock(mMutex);
+ return mDequeueError;
+ }
+
+ private:
+ sp<ANativeWindow> mANW;
+ status_t mDequeueError;
+ Mutex mMutex;
+ };
+
+ sp<FrameWaiter> fw(new FrameWaiter);
+ mST->setFrameAvailableListener(fw);
+ ASSERT_EQ(OK, mST->setSynchronousMode(true));
+ ASSERT_EQ(OK, mST->setBufferCountServer(2));
+
+ sp<Thread> pt(new ProducerThread(mANW));
+ pt->run();
+
+ fw->waitForFrame();
+ fw->waitForFrame();
+
+ // Sleep for 100ms to allow the producer thread's dequeueBuffer call to
+ // block waiting for a buffer to become available.
+ usleep(100000);
+
+ mST->abandon();
+
+ pt->requestExitAndWait();
+ ASSERT_EQ(NO_INIT,
+ reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
+}
+
/*
* This test is for testing GL -> GL texture streaming via SurfaceTexture. It
* contains functionality to create a producer thread that will perform GL
@@ -1201,7 +1278,7 @@
sp<FrameCondition> mFC;
};
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedWorks) {
+TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) {
class PT : public ProducerThread {
virtual void render() {
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1219,7 +1296,7 @@
// TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) {
+TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) {
class PT : public ProducerThread {
virtual void render() {
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1237,7 +1314,7 @@
// TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedWorks) {
+TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
enum { NUM_ITERATIONS = 1024 };
class PT : public ProducerThread {
@@ -1265,7 +1342,7 @@
}
}
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedWorks) {
+TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
enum { NUM_ITERATIONS = 1024 };
class PT : public ProducerThread {
@@ -1293,4 +1370,70 @@
}
}
+// XXX: This test is disabled because it is currently hanging on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
+ enum { NUM_ITERATIONS = 64 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ ASSERT_EQ(OK, mST->setSynchronousMode(true));
+ ASSERT_EQ(OK, mST->setBufferCountServer(2));
+
+ runProducerThread(new PT());
+
+ // Allow three frames to be rendered and queued before starting the
+ // rendering in this thread. For the latter two frames we don't call
+ // updateTexImage so the next dequeue from the producer thread will block
+ // waiting for a frame to become available.
+ mFC->waitForFrame();
+ mFC->finishFrame();
+
+ // We must call updateTexImage to consume the first frame so that the
+ // SurfaceTexture is able to reduce the buffer count to 2. This is because
+ // the GL driver may dequeue a buffer when the EGLSurface is created, and
+ // that happens before we call setBufferCountServer. It's possible that the
+ // driver does not dequeue a buffer at EGLSurface creation time, so we
+ // cannot rely on this to cause the second dequeueBuffer call to block.
+ mST->updateTexImage();
+
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ mFC->waitForFrame();
+ mFC->finishFrame();
+
+ // Sleep for 100ms to allow the producer thread's dequeueBuffer call to
+ // block waiting for a buffer to become available.
+ usleep(100000);
+
+ // Render and present a number of images. This thread should not be blocked
+ // by the fact that the producer thread is blocking in dequeue.
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(mEglDisplay, mEglSurface);
+ }
+
+ // Consume the two pending buffers to unblock the producer thread.
+ mST->updateTexImage();
+ mST->updateTexImage();
+
+ // Consume the remaining buffers from the producer thread.
+ for (int i = 0; i < NUM_ITERATIONS-3; i++) {
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+ }
+}
+
} // namespace android
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 31e4631..82e8d77 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -54,7 +54,8 @@
/** Default audio channel mask */
public static final int CHANNEL_OUT_DEFAULT = 1;
- // Channel mask definitions must be kept in sync with native values in include/media/AudioSystem.h
+ // Channel mask definitions must be kept in sync with native values
+ // in /system/core/include/system/audio.h
public static final int CHANNEL_OUT_FRONT_LEFT = 0x4;
public static final int CHANNEL_OUT_FRONT_RIGHT = 0x8;
public static final int CHANNEL_OUT_FRONT_CENTER = 0x10;
@@ -64,6 +65,25 @@
public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
+ /** @hide */
+ public static final int CHANNEL_OUT_SIDE_LEFT = 0x800;
+ /** @hide */
+ public static final int CHANNEL_OUT_SIDE_RIGHT = 0x1000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_CENTER = 0x2000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 0x4000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 0x8000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 0x10000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_BACK_LEFT = 0x20000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_BACK_CENTER = 0x40000;
+ /** @hide */
+ public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 0x80000;
+
public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
public static final int CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
@@ -75,6 +95,12 @@
public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
+ /** @hide */
+ public static final int CHANNEL_OUT_7POINT1_SURROUND = (
+ CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT |
+ CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+ CHANNEL_OUT_LOW_FREQUENCY);
public static final int CHANNEL_IN_DEFAULT = 1;
public static final int CHANNEL_IN_LEFT = 0x4;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 482b437..95671bc 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -611,7 +611,7 @@
* needed. Not calling this method when playing back a video will
* result in only the audio track being played.
*
- * Either a surface or surface texture must be set if a display or video sink
+ * Either a surface holder or surface must be set if a display or video sink
* is needed. Not calling this method or {@link #setTexture(SurfaceTexture)}
* when playing back a video will result in only the audio track being played.
*
@@ -630,6 +630,27 @@
}
/**
+ * Sets the {@link Surface} to be used as the sink for the video portion of
+ * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but does not
+ * support {@link #setScreenOnWhilePlaying(boolean)} or {@link #updateSurfaceScreenOn()}.
+ * Setting a Surface will un-set any Surface or SurfaceHolder that was previously set.
+ *
+ * @param surface The {@link Surface} to be used for the video portion of the media.
+ *
+ * @hide Pending review by API council.
+ */
+ public void setSurface(Surface surface) {
+ if (mScreenOnWhilePlaying && surface != null && mSurface != null) {
+ Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
+ }
+ mSurfaceHolder = null;
+ mSurface = surface;
+ mParcelSurfaceTexture = null; // TODO(tedbo): Remove.
+ _setVideoSurfaceOrSurfaceTexture();
+ updateSurfaceScreenOn();
+ }
+
+ /**
* Sets the {@link SurfaceTexture} to be used as the sink for the
* video portion of the media. Either a surface or surface texture
* must be set if a video sink is needed. The same surface texture
@@ -665,7 +686,7 @@
* @param pst The {@link ParcelSurfaceTexture} to be used as the sink for
* the video portion of the media.
*
- * @hide Pending review by API council.
+ * @hide Pending removal when there are no more callers.
*/
public void setParcelSurfaceTexture(ParcelSurfaceTexture pst) {
if (mScreenOnWhilePlaying && pst != null && mParcelSurfaceTexture == null) {
@@ -1000,8 +1021,8 @@
*/
public void setScreenOnWhilePlaying(boolean screenOn) {
if (mScreenOnWhilePlaying != screenOn) {
- if (screenOn && mParcelSurfaceTexture != null) {
- Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+ if (screenOn && mSurfaceHolder == null) {
+ Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
}
mScreenOnWhilePlaying = screenOn;
updateSurfaceScreenOn();
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index e3cbd57..72069ac 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -81,9 +81,6 @@
private String mPath;
private FileDescriptor mFd;
- private boolean mPrepareAuxiliaryFile = false;
- private String mPathAux;
- private FileDescriptor mFdAux;
private EventHandler mEventHandler;
private OnErrorListener mOnErrorListener;
private OnInfoListener mOnInfoListener;
@@ -557,84 +554,23 @@
}
/**
- * Sets the auxiliary time lapse video's resolution and bitrate.
- *
- * The auxiliary video's resolution and bitrate are determined by the CamcorderProfile
- * quality level {@link android.media.CamcorderProfile#QUALITY_HIGH}.
- */
- private void setAuxVideoParameters() {
- CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
- setParameter(String.format("video-aux-param-width=%d", profile.videoFrameWidth));
- setParameter(String.format("video-aux-param-height=%d", profile.videoFrameHeight));
- setParameter(String.format("video-aux-param-encoding-bitrate=%d", profile.videoBitRate));
- }
-
- /**
- * Pass in the file descriptor for the auxiliary time lapse video. Call this before
- * prepare().
- *
- * Sets file descriptor and parameters for auxiliary time lapse video. Time lapse mode
- * can capture video (using the still camera) at resolutions higher than that can be
- * played back on the device. This function or
- * {@link #setAuxiliaryOutputFile(String)} enable capture of a smaller video in
- * parallel with the main time lapse video, which can be used to play back on the
- * device. The smaller video is created by downsampling the main video. This call is
- * optional and does not have to be called if parallel capture of a downsampled video
- * is not desired.
- *
- * Note that while the main video resolution and bitrate is determined from the
- * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's
- * resolution and bitrate are determined by the CamcorderProfile quality level
- * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters
- * remain the same for the main video and the auxiliary video.
- *
- * E.g. if the device supports the time lapse profile quality level
- * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at
- * most 480p, the application might want to capture an auxiliary video of resolution
- * 480p using this call.
- *
- * @param fd an open file descriptor to be written into.
+ * Currently not implemented. It does nothing.
+ * @deprecated Time lapse mode video recording using camera still image capture
+ * is not desirable, and will not be supported.
*/
public void setAuxiliaryOutputFile(FileDescriptor fd)
{
- mPrepareAuxiliaryFile = true;
- mPathAux = null;
- mFdAux = fd;
- setAuxVideoParameters();
+ Log.w(TAG, "setAuxiliaryOutputFile(FileDescriptor) is no longer supported.");
}
/**
- * Pass in the file path for the auxiliary time lapse video. Call this before
- * prepare().
- *
- * Sets file path and parameters for auxiliary time lapse video. Time lapse mode can
- * capture video (using the still camera) at resolutions higher than that can be
- * played back on the device. This function or
- * {@link #setAuxiliaryOutputFile(FileDescriptor)} enable capture of a smaller
- * video in parallel with the main time lapse video, which can be used to play back on
- * the device. The smaller video is created by downsampling the main video. This call
- * is optional and does not have to be called if parallel capture of a downsampled
- * video is not desired.
- *
- * Note that while the main video resolution and bitrate is determined from the
- * CamcorderProfile in {@link #setProfile(CamcorderProfile)}, the auxiliary video's
- * resolution and bitrate are determined by the CamcorderProfile quality level
- * {@link android.media.CamcorderProfile#QUALITY_HIGH}. All other encoding parameters
- * remain the same for the main video and the auxiliary video.
- *
- * E.g. if the device supports the time lapse profile quality level
- * {@link android.media.CamcorderProfile#QUALITY_TIME_LAPSE_1080P} but can playback at
- * most 480p, the application might want to capture an auxiliary video of resolution
- * 480p using this call.
- *
- * @param path The pathname to use.
+ * Currently not implemented. It does nothing.
+ * @deprecated Time lapse mode video recording using camera still image capture
+ * is not desirable, and will not be supported.
*/
public void setAuxiliaryOutputFile(String path)
{
- mPrepareAuxiliaryFile = true;
- mFdAux = null;
- mPathAux = path;
- setAuxVideoParameters();
+ Log.w(TAG, "setAuxiliaryOutputFile(String) is no longer supported.");
}
/**
@@ -668,8 +604,6 @@
// native implementation
private native void _setOutputFile(FileDescriptor fd, long offset, long length)
throws IllegalStateException, IOException;
- private native void _setOutputFileAux(FileDescriptor fd)
- throws IllegalStateException, IOException;
private native void _prepare() throws IllegalStateException, IOException;
/**
@@ -696,21 +630,6 @@
throw new IOException("No valid output file");
}
- if (mPrepareAuxiliaryFile) {
- if (mPathAux != null) {
- FileOutputStream fos = new FileOutputStream(mPathAux);
- try {
- _setOutputFileAux(fos.getFD());
- } finally {
- fos.close();
- }
- } else if (mFdAux != null) {
- _setOutputFileAux(mFdAux);
- } else {
- throw new IOException("No valid output file");
- }
- }
-
_prepare();
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 12391c8..922f7ed 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -127,7 +127,7 @@
return false;
}
-static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz)
+sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
MediaRecorder* const p = (MediaRecorder*)env->GetIntField(thiz, fields.context);
@@ -261,20 +261,6 @@
}
static void
-android_media_MediaRecorder_setOutputFileAuxFD(JNIEnv *env, jobject thiz, jobject fileDescriptor)
-{
- LOGV("setOutputFile");
- if (fileDescriptor == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
- status_t opStatus = mr->setOutputFileAuxiliary(fd);
- process_media_recorder_call(env, opStatus, "java/io/IOException", "setOutputFile failed.");
-}
-
-static void
android_media_MediaRecorder_setVideoSize(JNIEnv *env, jobject thiz, jint width, jint height)
{
LOGV("setVideoSize(%d, %d)", width, height);
@@ -475,7 +461,6 @@
{"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder},
{"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter},
{"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD},
- {"_setOutputFileAux", "(Ljava/io/FileDescriptor;)V", (void *)android_media_MediaRecorder_setOutputFileAuxFD},
{"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize},
{"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate},
{"setMaxDuration", "(I)V", (void *)android_media_MediaRecorder_setMaxDuration},
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index a44ef5a..7e44c29 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -23,14 +23,17 @@
#include <camera/ICamera.h>
#include <media/IMediaRecorderClient.h>
#include <media/IMediaRecorder.h>
+#include <gui/ISurfaceTexture.h>
#include <unistd.h>
+
namespace android {
enum {
RELEASE = IBinder::FIRST_CALL_TRANSACTION,
INIT,
CLOSE,
+ QUERY_SURFACE_MEDIASOURCE,
RESET,
STOP,
START,
@@ -71,6 +74,19 @@
return reply.readInt32();
}
+ sp<ISurfaceTexture> querySurfaceMediaSource()
+ {
+ LOGV("Query SurfaceMediaSource");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ remote()->transact(QUERY_SURFACE_MEDIASOURCE, data, &reply);
+ int returnedNull = reply.readInt32();
+ if (returnedNull) {
+ return NULL;
+ }
+ return interface_cast<ISurfaceTexture>(reply.readStrongBinder());
+ }
+
status_t setPreviewSurface(const sp<Surface>& surface)
{
LOGV("setPreviewSurface(%p)", surface.get());
@@ -440,6 +456,20 @@
reply->writeInt32(setCamera(camera, proxy));
return NO_ERROR;
} break;
+ case QUERY_SURFACE_MEDIASOURCE: {
+ LOGV("QUERY_SURFACE_MEDIASOURCE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ // call the mediaserver side to create
+ // a surfacemediasource
+ sp<ISurfaceTexture> surfaceMediaSource = querySurfaceMediaSource();
+ // The mediaserver might have failed to create a source
+ int returnedNull= (surfaceMediaSource == NULL) ? 1 : 0 ;
+ reply->writeInt32(returnedNull);
+ if (!returnedNull) {
+ reply->writeStrongBinder(surfaceMediaSource->asBinder());
+ }
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 9e4edd0..fab674c 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -25,6 +25,7 @@
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/mediaplayer.h> // for MEDIA_ERROR_SERVER_DIED
+#include <gui/ISurfaceTexture.h>
namespace android {
@@ -127,7 +128,9 @@
return INVALID_OPERATION;
}
+ // following call is made over the Binder Interface
status_t ret = mMediaRecorder->setVideoSource(vs);
+
if (OK != ret) {
LOGV("setVideoSource failed: %d", ret);
mCurrentState = MEDIA_RECORDER_ERROR;
@@ -357,7 +360,7 @@
return INVALID_OPERATION;
}
if (!mIsVideoSourceSet) {
- LOGE("try to set video size without setting video source first");
+ LOGE("Cannot set video size without setting video source first");
return INVALID_OPERATION;
}
@@ -367,9 +370,27 @@
mCurrentState = MEDIA_RECORDER_ERROR;
return ret;
}
+
return ret;
}
+// Query a SurfaceMediaSurface through the Mediaserver, over the
+// binder interface. This is used by the Filter Framework (MeidaEncoder)
+// to get an <ISurfaceTexture> object to hook up to ANativeWindow.
+sp<ISurfaceTexture> MediaRecorder::
+ querySurfaceMediaSourceFromMediaServer()
+{
+ Mutex::Autolock _l(mLock);
+ mSurfaceMediaSource =
+ mMediaRecorder->querySurfaceMediaSource();
+ if (mSurfaceMediaSource == NULL) {
+ LOGE("SurfaceMediaSource could not be initialized!");
+ }
+ return mSurfaceMediaSource;
+}
+
+
+
status_t MediaRecorder::setVideoFrameRate(int frames_per_second)
{
LOGV("setVideoFrameRate(%d)", frames_per_second);
@@ -382,7 +403,7 @@
return INVALID_OPERATION;
}
if (!mIsVideoSourceSet) {
- LOGE("try to set video frame rate without setting video source first");
+ LOGE("Cannot set video frame rate without setting video source first");
return INVALID_OPERATION;
}
@@ -621,7 +642,7 @@
return INVALID_OPERATION;
}
-MediaRecorder::MediaRecorder()
+MediaRecorder::MediaRecorder() : mSurfaceMediaSource(NULL)
{
LOGV("constructor");
@@ -632,6 +653,8 @@
if (mMediaRecorder != NULL) {
mCurrentState = MEDIA_RECORDER_IDLE;
}
+
+
doCleanUp();
}
@@ -646,6 +669,10 @@
if (mMediaRecorder != NULL) {
mMediaRecorder.clear();
}
+
+ if (mSurfaceMediaSource != NULL) {
+ mSurfaceMediaSource.clear();
+ }
}
status_t MediaRecorder::setListener(const sp<MediaRecorderListener>& listener)
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 115db1a..905b885 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -41,6 +41,7 @@
#include "MediaPlayerService.h"
#include "StagefrightRecorder.h"
+#include <gui/ISurfaceTexture.h>
namespace android {
@@ -57,6 +58,20 @@
return ok;
}
+
+sp<ISurfaceTexture> MediaRecorderClient::querySurfaceMediaSource()
+{
+ LOGV("Query SurfaceMediaSource");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ LOGE("recorder is not initialized");
+ return NULL;
+ }
+ return mRecorder->querySurfaceMediaSource();
+}
+
+
+
status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera,
const sp<ICameraRecordingProxy>& proxy)
{
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index bbca529..c87a3c0 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -25,45 +25,51 @@
class MediaRecorderBase;
class MediaPlayerService;
class ICameraRecordingProxy;
+class ISurfaceTexture;
class MediaRecorderClient : public BnMediaRecorder
{
public:
- virtual status_t setCamera(const sp<ICamera>& camera,
- const sp<ICameraRecordingProxy>& proxy);
- virtual status_t setPreviewSurface(const sp<Surface>& surface);
- virtual status_t setVideoSource(int vs);
- virtual status_t setAudioSource(int as);
- virtual status_t setOutputFormat(int of);
- virtual status_t setVideoEncoder(int ve);
- virtual status_t setAudioEncoder(int ae);
- virtual status_t setOutputFile(const char* path);
- virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
- virtual status_t setOutputFileAuxiliary(int fd);
- virtual status_t setVideoSize(int width, int height);
- virtual status_t setVideoFrameRate(int frames_per_second);
- virtual status_t setParameters(const String8& params);
- virtual status_t setListener(const sp<IMediaRecorderClient>& listener);
- virtual status_t prepare();
- virtual status_t getMaxAmplitude(int* max);
- virtual status_t start();
- virtual status_t stop();
- virtual status_t reset();
- virtual status_t init();
- virtual status_t close();
- virtual status_t release();
+ virtual status_t setCamera(const sp<ICamera>& camera,
+ const sp<ICameraRecordingProxy>& proxy);
+ virtual status_t setPreviewSurface(const sp<Surface>& surface);
+ virtual status_t setVideoSource(int vs);
+ virtual status_t setAudioSource(int as);
+ virtual status_t setOutputFormat(int of);
+ virtual status_t setVideoEncoder(int ve);
+ virtual status_t setAudioEncoder(int ae);
+ virtual status_t setOutputFile(const char* path);
+ virtual status_t setOutputFile(int fd, int64_t offset,
+ int64_t length);
+ virtual status_t setOutputFileAuxiliary(int fd);
+ virtual status_t setVideoSize(int width, int height);
+ virtual status_t setVideoFrameRate(int frames_per_second);
+ virtual status_t setParameters(const String8& params);
+ virtual status_t setListener(
+ const sp<IMediaRecorderClient>& listener);
+ virtual status_t prepare();
+ virtual status_t getMaxAmplitude(int* max);
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t reset();
+ virtual status_t init();
+ virtual status_t close();
+ virtual status_t release();
+ virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual sp<ISurfaceTexture> querySurfaceMediaSource();
- virtual status_t dump(int fd, const Vector<String16>& args) const;
private:
- friend class MediaPlayerService; // for accessing private constructor
+ friend class MediaPlayerService; // for accessing private constructor
- MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid);
- virtual ~MediaRecorderClient();
+ MediaRecorderClient(
+ const sp<MediaPlayerService>& service,
+ pid_t pid);
+ virtual ~MediaRecorderClient();
- pid_t mPid;
- Mutex mLock;
- MediaRecorderBase *mRecorder;
- sp<MediaPlayerService> mMediaPlayerService;
+ pid_t mPid;
+ Mutex mLock;
+ MediaRecorderBase *mRecorder;
+ sp<MediaPlayerService> mMediaPlayerService;
};
}; // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 223e0be..6427bb7 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -38,10 +38,12 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/SurfaceMediaSource.h>
#include <media/MediaProfiles.h>
#include <camera/ICamera.h>
#include <camera/CameraParameters.h>
#include <surfaceflinger/Surface.h>
+
#include <utils/Errors.h>
#include <sys/types.h>
#include <ctype.h>
@@ -69,7 +71,7 @@
mOutputFd(-1), mOutputFdAux(-1),
mAudioSource(AUDIO_SOURCE_CNT),
mVideoSource(VIDEO_SOURCE_LIST_END),
- mStarted(false) {
+ mStarted(false), mSurfaceMediaSource(NULL) {
LOGV("Constructor");
reset();
@@ -85,6 +87,14 @@
return OK;
}
+// The client side of mediaserver asks it to creat a SurfaceMediaSource
+// and return a interface reference. The client side will use that
+// while encoding GL Frames
+sp<ISurfaceTexture> StagefrightRecorder::querySurfaceMediaSource() const {
+ LOGV("Get SurfaceMediaSource");
+ return mSurfaceMediaSource;
+}
+
status_t StagefrightRecorder::setAudioSource(audio_source_t as) {
LOGV("setAudioSource: %d", as);
if (as < AUDIO_SOURCE_DEFAULT ||
@@ -1006,13 +1016,13 @@
source = createAudioSource();
} else {
- sp<CameraSource> cameraSource;
- status_t err = setupCameraSource(&cameraSource);
+ sp<MediaSource> mediaSource;
+ status_t err = setupMediaSource(&mediaSource);
if (err != OK) {
return err;
}
- err = setupVideoEncoder(cameraSource, mVideoBitRate, &source);
+ err = setupVideoEncoder(mediaSource, mVideoBitRate, &source);
if (err != OK) {
return err;
}
@@ -1042,20 +1052,19 @@
}
}
- if (mVideoSource == VIDEO_SOURCE_DEFAULT
- || mVideoSource == VIDEO_SOURCE_CAMERA) {
+ if (mVideoSource < VIDEO_SOURCE_LIST_END) {
if (mVideoEncoder != VIDEO_ENCODER_H264) {
return ERROR_UNSUPPORTED;
}
- sp<CameraSource> cameraSource;
- status_t err = setupCameraSource(&cameraSource);
+ sp<MediaSource> mediaSource;
+ status_t err = setupMediaSource(&mediaSource);
if (err != OK) {
return err;
}
sp<MediaSource> encoder;
- err = setupVideoEncoder(cameraSource, mVideoBitRate, &encoder);
+ err = setupVideoEncoder(mediaSource, mVideoBitRate, &encoder);
if (err != OK) {
return err;
@@ -1289,6 +1298,60 @@
}
}
+// Set up the appropriate MediaSource depending on the chosen option
+status_t StagefrightRecorder::setupMediaSource(
+ sp<MediaSource> *mediaSource) {
+ if (mVideoSource == VIDEO_SOURCE_DEFAULT
+ || mVideoSource == VIDEO_SOURCE_CAMERA) {
+ sp<CameraSource> cameraSource;
+ status_t err = setupCameraSource(&cameraSource);
+ if (err != OK) {
+ return err;
+ }
+ *mediaSource = cameraSource;
+ } else if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {
+ // If using GRAlloc buffers, setup surfacemediasource.
+ // Later a handle to that will be passed
+ // to the client side when queried
+ status_t err = setupSurfaceMediaSource();
+ if (err != OK) {
+ return err;
+ }
+ *mediaSource = mSurfaceMediaSource;
+ } else {
+ return INVALID_OPERATION;
+ }
+ return OK;
+}
+
+// setupSurfaceMediaSource creates a source with the given
+// width and height and framerate.
+// TODO: This could go in a static function inside SurfaceMediaSource
+// similar to that in CameraSource
+status_t StagefrightRecorder::setupSurfaceMediaSource() {
+ status_t err = OK;
+ mSurfaceMediaSource = new SurfaceMediaSource(mVideoWidth, mVideoHeight);
+ if (mSurfaceMediaSource == NULL) {
+ return NO_INIT;
+ }
+
+ if (mFrameRate == -1) {
+ int32_t frameRate = 0;
+ CHECK (mSurfaceMediaSource->getFormat()->findInt32(
+ kKeyFrameRate, &frameRate));
+ LOGI("Frame rate is not explicitly set. Use the current frame "
+ "rate (%d fps)", frameRate);
+ mFrameRate = frameRate;
+ } else {
+ err = mSurfaceMediaSource->setFrameRate(mFrameRate);
+ }
+ CHECK(mFrameRate != -1);
+
+ mIsMetaDataStoredInVideoBuffers =
+ mSurfaceMediaSource->isMetaDataStoredInVideoBuffers();
+ return err;
+}
+
status_t StagefrightRecorder::setupCameraSource(
sp<CameraSource> *cameraSource) {
status_t err = OK;
@@ -1465,29 +1528,37 @@
status_t err = OK;
sp<MediaWriter> writer = new MPEG4Writer(outputFd);
- if (mVideoSource == VIDEO_SOURCE_DEFAULT
- || mVideoSource == VIDEO_SOURCE_CAMERA) {
+ if (mVideoSource < VIDEO_SOURCE_LIST_END) {
- sp<MediaSource> cameraMediaSource;
+ sp<MediaSource> mediaSource;
if (useSplitCameraSource) {
+ // TODO: Check if there is a better way to handle this
+ if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {
+ LOGE("Cannot use split camera when encoding frames");
+ return INVALID_OPERATION;
+ }
LOGV("Using Split camera source");
- cameraMediaSource = mCameraSourceSplitter->createClient();
+ mediaSource = mCameraSourceSplitter->createClient();
} else {
- sp<CameraSource> cameraSource;
- err = setupCameraSource(&cameraSource);
- cameraMediaSource = cameraSource;
+ err = setupMediaSource(&mediaSource);
}
+
if ((videoWidth != mVideoWidth) || (videoHeight != mVideoHeight)) {
+ // TODO: Might be able to handle downsampling even if using GRAlloc
+ if (mVideoSource == VIDEO_SOURCE_GRALLOC_BUFFER) {
+ LOGE("Cannot change size or Downsample when encoding frames");
+ return INVALID_OPERATION;
+ }
// Use downsampling from the original source.
- cameraMediaSource =
- new VideoSourceDownSampler(cameraMediaSource, videoWidth, videoHeight);
+ mediaSource =
+ new VideoSourceDownSampler(mediaSource, videoWidth, videoHeight);
}
if (err != OK) {
return err;
}
sp<MediaSource> encoder;
- err = setupVideoEncoder(cameraMediaSource, videoBitRate, &encoder);
+ err = setupVideoEncoder(mediaSource, videoBitRate, &encoder);
if (err != OK) {
return err;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 034b373..1618b92 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -36,6 +36,8 @@
class MetaData;
struct AudioSource;
class MediaProfiles;
+class ISurfaceTexture;
+class SurfaceMediaSource;
struct StagefrightRecorder : public MediaRecorderBase {
StagefrightRecorder();
@@ -64,6 +66,8 @@
virtual status_t reset();
virtual status_t getMaxAmplitude(int *max);
virtual status_t dump(int fd, const Vector<String16>& args) const;
+ // Querying a SurfaceMediaSourcer
+ virtual sp<ISurfaceTexture> querySurfaceMediaSource() const;
private:
sp<ICamera> mCamera;
@@ -109,12 +113,18 @@
sp<MediaSourceSplitter> mCameraSourceSplitter;
sp<CameraSourceTimeLapse> mCameraSourceTimeLapse;
+
String8 mParams;
bool mIsMetaDataStoredInVideoBuffers;
MediaProfiles *mEncoderProfiles;
bool mStarted;
+ // Needed when GLFrames are encoded.
+ // An <ISurfaceTexture> pointer
+ // will be sent to the client side using which the
+ // frame buffers will be queued and dequeued
+ sp<SurfaceMediaSource> mSurfaceMediaSource;
status_t setupMPEG4Recording(
bool useSplitCameraSource,
@@ -134,7 +144,14 @@
sp<MediaSource> createAudioSource();
status_t checkVideoEncoderCapabilities();
status_t checkAudioEncoderCapabilities();
+ // Generic MediaSource set-up. Returns the appropriate
+ // source (CameraSource or SurfaceMediaSource)
+ // depending on the videosource type
+ status_t setupMediaSource(sp<MediaSource> *mediaSource);
status_t setupCameraSource(sp<CameraSource> *cameraSource);
+ // setup the surfacemediasource for the encoder
+ status_t setupSurfaceMediaSource();
+
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
status_t setupVideoEncoder(
sp<MediaSource> cameraSource,
@@ -176,6 +193,7 @@
void clipNumberOfAudioChannels();
void setDefaultProfileIfNecessary();
+
StagefrightRecorder(const StagefrightRecorder &);
StagefrightRecorder &operator=(const StagefrightRecorder &);
};
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7bcbdcf..ac73351 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -48,6 +48,10 @@
namespace android {
+// Treat time out as an error if we have not received any output
+// buffers after 3 seconds.
+const static int64_t kBufferFilledEventTimeOutUs = 3000000000LL;
+
struct CodecInfo {
const char *mime;
const char *codec;
@@ -3191,6 +3195,16 @@
mBufferFilled.signal();
}
+status_t OMXCodec::waitForBufferFilled_l() {
+ status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutUs);
+ if (err != OK) {
+ LOGE("Timed out waiting for buffers from video encoder: %d/%d",
+ countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
+ countBuffersWeOwn(mPortBuffers[kPortIndexOutput]));
+ }
+ return err;
+}
+
void OMXCodec::setRawAudioFormat(
OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) {
@@ -3623,6 +3637,7 @@
status_t OMXCodec::read(
MediaBuffer **buffer, const ReadOptions *options) {
+ status_t err = OK;
*buffer = NULL;
Mutex::Autolock autoLock(mLock);
@@ -3663,7 +3678,9 @@
if (seeking) {
while (mState == RECONFIGURING) {
- mBufferFilled.wait(mLock);
+ if ((err = waitForBufferFilled_l()) != OK) {
+ return err;
+ }
}
if (mState != EXECUTING) {
@@ -3694,19 +3711,15 @@
}
while (mSeekTimeUs >= 0) {
- mBufferFilled.wait(mLock);
+ if ((err = waitForBufferFilled_l()) != OK) {
+ return err;
+ }
}
}
while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
- if (mIsEncoder) {
- if (NO_ERROR != mBufferFilled.waitRelative(mLock, 3000000000LL)) {
- LOGW("Timed out waiting for buffers from video encoder: %d/%d",
- countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
- countBuffersWeOwn(mPortBuffers[kPortIndexOutput]));
- }
- } else {
- mBufferFilled.wait(mLock);
+ if ((err = waitForBufferFilled_l()) != OK) {
+ return err;
}
}
@@ -4415,6 +4428,13 @@
return OK;
}
+status_t QueryCodecs(
+ const sp<IOMX> &omx,
+ const char *mimeType, bool queryDecoders,
+ Vector<CodecCapabilities> *results) {
+ return QueryCodecs(omx, mimeType, queryDecoders, false /*hwCodecOnly*/, results);
+}
+
void OMXCodec::restorePatchedDataPointer(BufferInfo *info) {
CHECK(mIsEncoder && (mQuirks & kAvoidMemcopyInputRecordingFrames));
CHECK(mOMXLivesLocally);
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index db208dd9..ff4b08f 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -35,18 +35,18 @@
namespace android {
SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) :
- mDefaultWidth(bufW),
- mDefaultHeight(bufH),
- mPixelFormat(0),
- mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
- mClientBufferCount(0),
- mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
- mCurrentSlot(INVALID_BUFFER_SLOT),
- mCurrentTimestamp(0),
- mSynchronousMode(true),
- mConnectedApi(NO_CONNECTED_API),
- mFrameRate(30),
- mStarted(false) {
+ mDefaultWidth(bufW),
+ mDefaultHeight(bufH),
+ mPixelFormat(0),
+ mBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mClientBufferCount(0),
+ mServerBufferCount(MIN_ASYNC_BUFFER_SLOTS),
+ mCurrentSlot(INVALID_BUFFER_SLOT),
+ mCurrentTimestamp(0),
+ mSynchronousMode(true),
+ mConnectedApi(NO_CONNECTED_API),
+ mFrameRate(30),
+ mStarted(false) {
LOGV("SurfaceMediaSource::SurfaceMediaSource");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -147,16 +147,17 @@
return OK;
}
-sp<GraphicBuffer> SurfaceMediaSource::requestBuffer(int buf) {
+status_t SurfaceMediaSource::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
LOGV("SurfaceMediaSource::requestBuffer");
Mutex::Autolock lock(mMutex);
- if (buf < 0 || mBufferCount <= buf) {
+ if (slot < 0 || mBufferCount <= slot) {
LOGE("requestBuffer: slot index out of range [0, %d]: %d",
- mBufferCount, buf);
- return 0;
+ mBufferCount, slot);
+ return BAD_VALUE;
}
- mSlots[buf].mRequestBufferCalled = true;
- return mSlots[buf].mGraphicBuffer;
+ mSlots[slot].mRequestBufferCalled = true;
+ *buf = mSlots[slot].mGraphicBuffer;
+ return NO_ERROR;
}
status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
@@ -372,7 +373,7 @@
status_t SurfaceMediaSource::connect(int api) {
LOGV("SurfaceMediaSource::connect");
Mutex::Autolock lock(mMutex);
- int err = NO_ERROR;
+ status_t err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
case NATIVE_WINDOW_API_CPU:
@@ -394,7 +395,7 @@
status_t SurfaceMediaSource::disconnect(int api) {
LOGV("SurfaceMediaSource::disconnect");
Mutex::Autolock lock(mMutex);
- int err = NO_ERROR;
+ status_t err = NO_ERROR;
switch (api) {
case NATIVE_WINDOW_API_EGL:
case NATIVE_WINDOW_API_CPU:
@@ -525,12 +526,6 @@
mFrameAvailableListener = listener;
}
-sp<IBinder> SurfaceMediaSource::getAllocator() {
- LOGV("getAllocator");
- return mGraphicBufferAlloc->asBinder();
-}
-
-
void SurfaceMediaSource::freeAllBuffers() {
LOGV("freeAllBuffers");
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
@@ -625,13 +620,23 @@
}
}
-void SurfaceMediaSource::setFrameRate(uint32_t fps)
+status_t SurfaceMediaSource::setFrameRate(int32_t fps)
{
Mutex::Autolock lock(mMutex);
+ const int MAX_FRAME_RATE = 60;
+ if (fps < 0 || fps > MAX_FRAME_RATE) {
+ return BAD_VALUE;
+ }
mFrameRate = fps;
+ return OK;
}
-uint32_t SurfaceMediaSource::getFrameRate( ) const {
+bool SurfaceMediaSource::isMetaDataStoredInVideoBuffers() const {
+ LOGV("isMetaDataStoredInVideoBuffers");
+ return true;
+}
+
+int32_t SurfaceMediaSource::getFrameRate( ) const {
Mutex::Autolock lock(mMutex);
return mFrameRate;
}
@@ -663,8 +668,7 @@
LOGV("getFormat");
Mutex::Autolock autoLock(mMutex);
sp<MetaData> meta = new MetaData;
- // XXX: Check if this is right. or should we wait on some
- // condition?
+
meta->setInt32(kKeyWidth, mDefaultWidth);
meta->setInt32(kKeyHeight, mDefaultHeight);
// The encoder format is set as an opaque colorformat
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index bf978d7..c406964 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -370,7 +370,9 @@
int16_t *dst = (int16_t *)tmp->data();
const uint8_t *src = (const uint8_t *)buffer->data();
- while (n-- > 0) {
+ ssize_t numBytes = n;
+
+ while (numBytes-- > 0) {
*dst++ = ((int16_t)(*src) - 128) * 256;
++src;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index e0debf7..bfc3cdd 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2040,13 +2040,14 @@
mActionModePopup = new PopupWindow(mContext, null,
com.android.internal.R.attr.actionModePopupWindowStyle);
mActionModePopup.setLayoutInScreenEnabled(true);
+ mActionModePopup.setLayoutInsetDecor(true);
mActionModePopup.setClippingEnabled(false);
mActionModePopup.setContentView(mActionModeView);
mActionModePopup.setWidth(MATCH_PARENT);
TypedValue heightValue = new TypedValue();
mContext.getTheme().resolveAttribute(
- com.android.internal.R.attr.actionBarSize, heightValue, false);
+ com.android.internal.R.attr.actionBarSize, heightValue, true);
final int height = TypedValue.complexToDimensionPixelSize(heightValue.data,
mContext.getResources().getDisplayMetrics());
mActionModePopup.setHeight(height);
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index ea78b52..79c0675 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -2262,6 +2262,15 @@
}
}
+ public int setUsbTethering(boolean enable) {
+ enforceTetherAccessPermission();
+ if (isTetheringSupported()) {
+ return mTethering.setUsbTethering(enable);
+ } else {
+ return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+ }
+ }
+
// TODO - move iface listing, queries, etc to new module
// javadoc from interface
public String[] getTetherableIfaces() {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index d39f565f..73d790a 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1127,13 +1127,21 @@
mBackDisposition = backDisposition;
mStatusBar.setImeWindowStatus(token, vis, backDisposition);
final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0;
- if (iconVisibility && needsToShowImeSwitchOngoingNotification()) {
+ final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
+ if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) {
final PackageManager pm = mContext.getPackageManager();
- final CharSequence label = mMethodMap.get(mCurMethodId).loadLabel(pm);
final CharSequence title = mRes.getText(
com.android.internal.R.string.select_input_method);
+ final CharSequence imiLabel = imi.loadLabel(pm);
+ final CharSequence summary = mCurrentSubtype != null
+ ? TextUtils.concat(mCurrentSubtype.getDisplayName(mContext,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo),
+ (TextUtils.isEmpty(imiLabel) ?
+ "" : " (" + imiLabel + ")"))
+ : imiLabel;
+
mImeSwitcherNotification.setLatestEventInfo(
- mContext, title, label, mImeSwitchPendingIntent);
+ mContext, title, summary, mImeSwitchPendingIntent);
mNotificationManager.notify(
com.android.internal.R.string.select_input_method,
mImeSwitcherNotification);
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 2366fcb..17ad268 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -849,7 +849,6 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
try {
- mConnector.doCommand(String.format("softap stop " + wlanIface));
mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
mConnector.doCommand(String.format("softap start " + wlanIface));
if (wifiConfig == null) {
@@ -897,13 +896,15 @@
}
}
- public void stopAccessPoint() throws IllegalStateException {
+ public void stopAccessPoint(String wlanIface) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
try {
mConnector.doCommand("softap stopap");
+ mConnector.doCommand("softap stop " + wlanIface);
+ mConnector.doCommand(String.format("softap fwreload " + wlanIface + " STA"));
} catch (NativeDaemonConnectorException e) {
throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
e);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 76665915..f15eca6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,6 +37,7 @@
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
+import android.server.WifiP2pService;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
@@ -108,6 +109,7 @@
NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
+ WifiP2pService wifiP2p = null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
@@ -299,6 +301,14 @@
Slog.e(TAG, "Failure starting NetworkPolicy Service", e);
}
+ try {
+ Slog.i(TAG, "Wi-Fi P2pService");
+ wifiP2p = new WifiP2pService(context);
+ ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Wi-Fi P2pService", e);
+ }
+
try {
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 4a0c837..ffdc288 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -156,6 +156,9 @@
final String curSpellCheckerId =
Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.SPELL_CHECKER_SERVICE);
+ if (DBG) {
+ Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
+ }
if (TextUtils.isEmpty(curSpellCheckerId)) {
return null;
}
@@ -198,6 +201,11 @@
}
@Override
+ public SpellCheckerInfo[] getEnabledSpellCheckers() {
+ return mSpellCheckerList.toArray(new SpellCheckerInfo[mSpellCheckerList.size()]);
+ }
+
+ @Override
public void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
synchronized(mSpellCheckerMap) {
for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) {
@@ -208,6 +216,9 @@
}
private void setCurrentSpellChecker(SpellCheckerInfo sci) {
+ if (DBG) {
+ Slog.w(TAG, "setCurrentSpellChecker: " + sci.getId());
+ }
if (sci == null || mSpellCheckerMap.containsKey(sci.getId())) return;
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.SPELL_CHECKER_SERVICE, sci == null ? "" : sci.getId());
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 82bc505..a5a6d8e 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -36,7 +36,6 @@
import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.os.Binder;
-import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -76,10 +75,6 @@
private final static String TAG = "Tethering";
private final static boolean DEBUG = true;
- private boolean mBooted = false;
- //used to remember if we got connected before boot finished
- private boolean mDeferedUsbConnection = false;
-
// TODO - remove both of these - should be part of interface inspection/selection stuff
private String[] mTetherableUsbRegexs;
private String[] mTetherableWifiRegexs;
@@ -126,10 +121,9 @@
private Notification mTetheredNotification;
- // whether we can tether is the && of these two - they come in as separate
- // broadcasts so track them so we can decide what to do when either changes
- private boolean mUsbMassStorageOff; // track the status of USB Mass Storage
- private boolean mUsbConnected; // track the status of USB connection
+ private boolean mRndisEnabled; // track the RNDIS function enabled state
+ private boolean mUsbTetherRequested; // true if USB tethering should be started
+ // when RNDIS is enabled
public Tethering(Context context, INetworkManagementService nmService, Looper looper) {
mContext = context;
@@ -149,7 +143,6 @@
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mStateReceiver, filter);
filter = new IntentFilter();
@@ -158,9 +151,6 @@
filter.addDataScheme("file");
mContext.registerReceiver(mStateReceiver, filter);
- mUsbMassStorageOff = !Environment.MEDIA_SHARED.equals(
- Environment.getExternalStorageState());
-
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
@@ -221,6 +211,8 @@
}
public void interfaceLinkStateChanged(String iface, boolean up) {
+ if (DEBUG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up);
+ interfaceStatusChanged(iface, up);
}
private boolean isUsb(String iface) {
@@ -243,6 +235,7 @@
}
return false;
}
+
public void interfaceAdded(String iface) {
boolean found = false;
boolean usb = false;
@@ -456,47 +449,28 @@
}
}
- private void updateUsbStatus() {
- boolean enable = mUsbConnected && mUsbMassStorageOff;
-
- if (mBooted) {
- enableUsbIfaces(enable);
- }
- }
-
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_STATE)) {
- mUsbConnected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
- updateUsbStatus();
- } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
- mUsbMassStorageOff = false;
- updateUsbStatus();
- }
- else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
- mUsbMassStorageOff = true;
- updateUsbStatus();
+ synchronized (Tethering.this) {
+ boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false);
+ // start tethering if we have a request pending
+ if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+ tetherUsb(true);
+ }
+ mUsbTetherRequested = false;
+ }
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
if (DEBUG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
- } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- mBooted = true;
- updateUsbStatus();
}
}
}
- // used on cable insert/remove
- private void enableUsbIfaces(boolean enable) {
- // add/remove USB interfaces when USB is connected/disconnected
- for (String intf : mTetherableUsbRegexs) {
- if (enable) {
- interfaceAdded(intf);
- } else {
- interfaceRemoved(intf);
- }
- }
+ private void tetherUsb(boolean enable) {
+ if (DEBUG) Log.d(TAG, "tetherUsb " + enable);
String[] ifaces = new String[0];
try {
@@ -507,83 +481,50 @@
}
for (String iface : ifaces) {
if (isUsb(iface)) {
- if (enable) {
- interfaceAdded(iface);
- } else {
- interfaceRemoved(iface);
+ int result = (enable ? tether(iface) : untether(iface));
+ if (result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ return;
}
}
}
- }
-
- // toggled when we enter/leave the fully tethered state
- private boolean enableUsbRndis(boolean enabled) {
- if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
-
- UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
- if (usbManager == null) {
- Log.d(TAG, "could not get UsbManager");
- return false;
- }
- try {
- if (enabled) {
- usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
- } else {
- usbManager.setCurrentFunction(null, false);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error toggling usb RNDIS", e);
- return false;
- }
- return true;
+ Log.e(TAG, "unable start or stop USB tethering");
}
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsbIface(boolean enabled) {
if (DEBUG) Log.d(TAG, "configureUsbIface(" + enabled + ")");
- if (enabled) {
- // must enable RNDIS first to create the interface
- enableUsbRndis(enabled);
- }
-
+ // toggle the USB interfaces
+ String[] ifaces = new String[0];
try {
- // bring toggle the interfaces
- String[] ifaces = new String[0];
- try {
- ifaces = mNMService.listInterfaces();
- } catch (Exception e) {
- Log.e(TAG, "Error listing Interfaces", e);
- return false;
- }
- for (String iface : ifaces) {
- if (isUsb(iface)) {
- InterfaceConfiguration ifcg = null;
- try {
- ifcg = mNMService.getInterfaceConfig(iface);
- if (ifcg != null) {
- InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
- ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
- if (enabled) {
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
- } else {
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
- }
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
- ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
- mNMService.setInterfaceConfig(iface, ifcg);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error configuring interface " + iface, e);
- return false;
- }
- }
- }
- } finally {
- if (!enabled) {
- enableUsbRndis(false);
- }
+ ifaces = mNMService.listInterfaces();
+ } catch (Exception e) {
+ Log.e(TAG, "Error listing Interfaces", e);
+ return false;
}
+ for (String iface : ifaces) {
+ if (isUsb(iface)) {
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = mNMService.getInterfaceConfig(iface);
+ if (ifcg != null) {
+ InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
+ ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
+ if (enabled) {
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+ } else {
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+ }
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
+ mNMService.setInterfaceConfig(iface, ifcg);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + iface, e);
+ return false;
+ }
+ }
+ }
return true;
}
@@ -600,6 +541,28 @@
return mTetherableBluetoothRegexs;
}
+ public int setUsbTethering(boolean enable) {
+ UsbManager usbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
+
+ synchronized (this) {
+ if (enable) {
+ if (mRndisEnabled) {
+ tetherUsb(true);
+ } else {
+ mUsbTetherRequested = true;
+ usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
+ }
+ } else {
+ tetherUsb(false);
+ if (mRndisEnabled) {
+ usbManager.setCurrentFunction(null, false);
+ }
+ mUsbTetherRequested = false;
+ }
+ }
+ return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ }
+
public int[] getUpstreamIfaceTypes() {
int values[] = new int[mUpstreamIfaceTypes.size()];
Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index cf75d6b..9cb772e 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -396,7 +396,7 @@
if (mTimer == -1) {
mTimer = now;
Thread.sleep(1);
- } else if (now - mTimer <= 30000) {
+ } else if (now - mTimer <= 60000) {
Thread.sleep(yield ? 200 : 1);
} else {
mInfo.state = LegacyVpnInfo.STATE_TIMEOUT;
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 40659d4..91e010f 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -64,7 +64,7 @@
if (orientation & Transform::ROT_INVALID) {
orientation = 0;
}
- *outTransform = layer->getOrientation();
+ *outTransform = orientation;
}
return res;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 4f8b525..3bd78e04 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -36,6 +36,7 @@
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.telephony.ServiceState;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
@@ -1031,6 +1032,27 @@
protected void onSetDependencyMet(String apnType, boolean met) {
}
+ protected String getReryConfig(boolean forDefault) {
+ int rt = mPhone.getServiceState().getRadioTechnology();
+
+ if ((rt == ServiceState.RADIO_TECHNOLOGY_IS95A) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_IS95B) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_1xRTT) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_0) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_A) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_EVDO_B) ||
+ (rt == ServiceState.RADIO_TECHNOLOGY_EHRPD)) {
+ // CDMA variant
+ return SystemProperties.get("ro.cdma.data_retry_config");
+ } else {
+ // Use GSM varient for all others.
+ if (forDefault) {
+ return SystemProperties.get("ro.gsm.data_retry_config");
+ } else {
+ return SystemProperties.get("ro.gsm.2nd_data_retry_config");
+ }
+ }
+ }
protected void resetAllRetryCounts() {
for (DataConnection dc : mDataConnections.values()) {
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 93fc9ce..facee5f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -31,6 +31,8 @@
// From the top of ril.cpp
int RIL_ERRNO_INVALID_RESPONSE = -1;
+ int MAX_INT = 0x7FFFFFFF;
+
// from RIL_Errno
int SUCCESS = 0;
int RADIO_NOT_AVAILABLE = 1; /* If radio did not start or is resetting */
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 5df2edd..d3bb6a5 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -247,10 +247,11 @@
boolean hasLocationChanged = !newCellLoc.equals(cellLoc);
boolean has4gHandoff =
- ((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) &&
- (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) ||
- ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) &&
- (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE));
+ mNewDataConnectionState == ServiceState.STATE_IN_SERVICE &&
+ (((networkType == ServiceState.RADIO_TECHNOLOGY_LTE) &&
+ (newNetworkType == ServiceState.RADIO_TECHNOLOGY_EHRPD)) ||
+ ((networkType == ServiceState.RADIO_TECHNOLOGY_EHRPD) &&
+ (newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE)));
boolean hasMultiApnSupport =
(((newNetworkType == ServiceState.RADIO_TECHNOLOGY_LTE) ||
@@ -391,7 +392,7 @@
phone.notifyServiceStateChanged(ss);
}
- if (hasCdmaDataConnectionAttached) {
+ if (hasCdmaDataConnectionAttached || has4gHandoff) {
mAttachedRegistrants.notifyRegistrants();
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index ccdb0bf..be129d5 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -609,9 +609,20 @@
for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
if (dcac.getReconnectIntentSync() != null) {
cancelReconnectAlarm(dcac);
- if (dcac.dataConnection != null) {
- dcac.dataConnection.resetRetryCount();
+ }
+ // update retry config for existing calls to match up
+ // ones for the new RAT.
+ if (dcac.dataConnection != null) {
+ Collection<ApnContext> apns = dcac.getApnListSync();
+
+ boolean hasDefault = false;
+ for (ApnContext apnContext : apns) {
+ if (apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
+ hasDefault = true;
+ break;
+ }
}
+ configureRetry(dcac.dataConnection, hasDefault);
}
}
@@ -973,7 +984,7 @@
// configure retry count if no other Apn is using the same connection.
if (refCount == 0) {
- configureRetry(dc, apnContext.getApnType());
+ configureRetry(dc, apn.canHandleType(Phone.APN_TYPE_DEFAULT));
}
apnContext.setDataConnectionAc(dcac);
apnContext.setDataConnection(dc);
@@ -1834,7 +1845,11 @@
retryOverride =
((DataConnection.CallSetupException)ar.exception).getRetryOverride();
}
- startDelayedRetry(cause, apnContext, retryOverride);
+ if (retryOverride == RILConstants.MAX_INT) {
+ if (DBG) log("No retry is suggested.");
+ } else {
+ startDelayedRetry(cause, apnContext, retryOverride);
+ }
}
} else {
if (DBG) log("onDataSetupComplete: Try next APN");
@@ -2022,20 +2037,18 @@
return conn;
}
- private void configureRetry(DataConnection dc, String apnType) {
- if ((dc == null) || (apnType == null)) return;
+ private void configureRetry(DataConnection dc, boolean forDefault) {
+ if (dc == null) return;
- if (apnType.equals(Phone.APN_TYPE_DEFAULT)) {
- if (!dc.configureRetry(SystemProperties.get("ro.gsm.data_retry_config"))) {
+ if (!dc.configureRetry(getReryConfig(forDefault))) {
+ if (forDefault) {
if (!dc.configureRetry(DEFAULT_DATA_RETRY_CONFIG)) {
// Should never happen, log an error and default to a simple linear sequence.
loge("configureRetry: Could not configure using " +
"DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
dc.configureRetry(20, 2000, 1000);
}
- }
- } else {
- if (!dc.configureRetry(SystemProperties.get("ro.gsm.2nd_data_retry_config"))) {
+ } else {
if (!dc.configureRetry(SECONDARY_DATA_RETRY_CONFIG)) {
// Should never happen, log an error and default to a simple sequence.
loge("configureRetry: Could note configure using " +
diff --git a/wifi/java/android/net/wifi/StateChangeResult.java b/wifi/java/android/net/wifi/StateChangeResult.java
new file mode 100644
index 0000000..8ab5982
--- /dev/null
+++ b/wifi/java/android/net/wifi/StateChangeResult.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.net.wifi;
+
+/**
+ * Stores supplicant state change information passed from WifiMonitor to
+ * a state machine. WifiStateMachine, SupplicantStateTracker and WpsStateMachine
+ * are example state machines that handle it.
+ * @hide
+ */
+public class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, SupplicantState state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ SupplicantState state;
+}
diff --git a/wifi/java/android/net/wifi/SupplicantStateTracker.java b/wifi/java/android/net/wifi/SupplicantStateTracker.java
index 0c4f9f6..9168e62 100644
--- a/wifi/java/android/net/wifi/SupplicantStateTracker.java
+++ b/wifi/java/android/net/wifi/SupplicantStateTracker.java
@@ -19,7 +19,7 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import android.net.wifi.WifiStateMachine.StateChangeResult;
+import android.net.wifi.StateChangeResult;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -159,11 +159,11 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
- case WifiStateMachine.AUTHENTICATION_FAILURE_EVENT:
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mAuthenticationFailuresCount++;
mAuthFailureInSupplicantBroadcast = true;
break;
- case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
@@ -251,7 +251,7 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
- case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
if (SupplicantState.isHandshakeState(state)) {
@@ -293,7 +293,7 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 4ec4cfc..2ccc8a2 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -16,15 +16,24 @@
package android.net.wifi;
-import android.util.Log;
import android.net.NetworkInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.StateChangeResult;
+import android.os.Message;
+import android.util.Log;
+
+
+import com.android.internal.util.Protocol;
+import com.android.internal.util.StateMachine;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateMachine} for handling. Runs in its own thread.
+ * to the {@link StateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -45,16 +54,16 @@
private static final int UNKNOWN = 9;
/** All events coming from the supplicant start with this prefix */
- private static final String eventPrefix = "CTRL-EVENT-";
- private static final int eventPrefixLen = eventPrefix.length();
+ private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
+ private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length();
/** All WPA events coming from the supplicant start with this prefix */
- private static final String wpaEventPrefix = "WPA:";
- private static final String passwordKeyMayBeIncorrectEvent =
+ private static final String WPA_EVENT_PREFIX_STR = "WPA:";
+ private static final String PASSWORD_MAY_BE_INCORRECT_STR =
"pre-shared key may be incorrect";
/* WPS events */
- private static final String wpsOverlapEvent = "WPS-OVERLAP-DETECTED";
+ private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
/**
* Names of events from wpa_supplicant (minus the prefix). In the
@@ -68,26 +77,26 @@
* </pre>
* <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
*/
- private static final String connectedEvent = "CONNECTED";
+ private static final String CONNECTED_STR = "CONNECTED";
/**
* <pre>
* CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
* </pre>
*/
- private static final String disconnectedEvent = "DISCONNECTED";
+ private static final String DISCONNECTED_STR = "DISCONNECTED";
/**
* <pre>
* CTRL-EVENT-STATE-CHANGE x
* </pre>
* <code>x</code> is the numerical value of the new state.
*/
- private static final String stateChangeEvent = "STATE-CHANGE";
+ private static final String STATE_CHANGE_STR = "STATE-CHANGE";
/**
* <pre>
* CTRL-EVENT-SCAN-RESULTS ready
* </pre>
*/
- private static final String scanResultsEvent = "SCAN-RESULTS";
+ private static final String SCAN_RESULTS_STR = "SCAN-RESULTS";
/**
* <pre>
@@ -95,32 +104,32 @@
* </pre>
* {@code x} is the link speed in Mb/sec.
*/
- private static final String linkSpeedEvent = "LINK-SPEED";
+ private static final String LINK_SPEED_STR = "LINK-SPEED";
/**
* <pre>
* CTRL-EVENT-TERMINATING - signal x
* </pre>
* <code>x</code> is the signal that caused termination.
*/
- private static final String terminatingEvent = "TERMINATING";
+ private static final String TERMINATING_STR = "TERMINATING";
/**
* <pre>
* CTRL-EVENT-DRIVER-STATE state
* </pre>
* <code>state</code> can be HANGED
*/
- private static final String driverStateEvent = "DRIVER-STATE";
+ private static final String DRIVER_STATE_STR = "DRIVER-STATE";
/**
* <pre>
* CTRL-EVENT-EAP-FAILURE EAP authentication failed
* </pre>
*/
- private static final String eapFailureEvent = "EAP-FAILURE";
+ private static final String EAP_FAILURE_STR = "EAP-FAILURE";
/**
* This indicates an authentication failure on EAP FAILURE event
*/
- private static final String eapAuthFailure = "EAP authentication failed";
+ private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
/**
* Regex pattern for extracting an Ethernet-style MAC address from a string.
@@ -130,17 +139,116 @@
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateMachine mWifiStateMachine;
+ /** P2P events */
+ private static final String P2P_EVENT_PREFIX_STR = "P2P";
+
+ /* P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1
+ name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0 */
+ private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND";
+
+ /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
+ private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
+
+ /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
+ private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
+
+ private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
+
+ private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
+
+ private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
+ "P2P-GROUP-FORMATION-SUCCESS";
+
+ private static final String P2P_GROUP_FORMATION_FAILURE_STR =
+ "P2P-GROUP-FORMATION-FAILURE";
+
+ /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
+ [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
+ go_dev_addr=fa:7b:7a:42:02:13 */
+ private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
+
+ /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
+ private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED";
+
+ /* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
+ bssid=fa:7b:7a:42:82:13 unknown-network */
+ private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED";
+
+ /* P2P-INVITATION-RESULT status=1 */
+ private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT";
+
+ /* P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ group_capab=0x0 */
+ private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
+ /* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ group_capab=0x0 */
+ private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN";
+ /* P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
+ pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ group_capab=0x0 */
+ private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
+
+ private static final String HOST_AP_EVENT_PREFIX_STR = "AP";
+ /* AP-STA-CONNECTED 42:fc:89:a8:96:09 */
+ private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
+ /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
+ private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
+
+ private final StateMachine mStateMachine;
+
+ /* Supplicant events reported to a state machine */
+ private static final int BASE = Protocol.BASE_WIFI_MONITOR;
+
+ /* Connection to supplicant established */
+ public static final int SUP_CONNECTION_EVENT = BASE + 1;
+ /* Connection to supplicant lost */
+ public static final int SUP_DISCONNECTION_EVENT = BASE + 2;
+ /* Network connection completed */
+ public static final int NETWORK_CONNECTION_EVENT = BASE + 3;
+ /* Network disconnection completed */
+ public static final int NETWORK_DISCONNECTION_EVENT = BASE + 4;
+ /* Scan results are available */
+ public static final int SCAN_RESULTS_EVENT = BASE + 5;
+ /* Supplicate state changed */
+ public static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 6;
+ /* Password failure and EAP authentication failure */
+ public static final int AUTHENTICATION_FAILURE_EVENT = BASE + 7;
+ /* WPS overlap detected */
+ public static final int WPS_OVERLAP_EVENT = BASE + 8;
+ /* Driver was hung */
+ public static final int DRIVER_HUNG_EVENT = BASE + 9;
+
+ /* P2P events */
+ public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21;
+ public static final int P2P_DEVICE_LOST_EVENT = BASE + 22;
+ public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT = BASE + 23;
+ public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT = BASE + 25;
+ public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT = BASE + 26;
+ public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT = BASE + 27;
+ public static final int P2P_GROUP_FORMATION_FAILURE_EVENT = BASE + 28;
+ public static final int P2P_GROUP_STARTED_EVENT = BASE + 29;
+ public static final int P2P_GROUP_REMOVED_EVENT = BASE + 30;
+ public static final int P2P_INVITATION_RECEIVED_EVENT = BASE + 31;
+ public static final int P2P_INVITATION_RESULT_EVENT = BASE + 32;
+ public static final int P2P_PROV_DISC_PBC_REQ_EVENT = BASE + 33;
+ public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 34;
+ public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 35;
+
+ /* hostap events */
+ public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41;
+ public static final int AP_STA_CONNECTED_EVENT = BASE + 42;
/**
* This indicates the supplicant connection for the monitor is closed
*/
- private static final String monitorSocketClosed = "connection closed";
+ private static final String MONITOR_SOCKET_CLOSED_STR = "connection closed";
/**
* This indicates a read error on the monitor socket conenction
*/
- private static final String wpaRecvError = "recv error";
+ private static final String WPA_RECV_ERROR_STR = "recv error";
/**
* Tracks consecutive receive errors
@@ -152,8 +260,8 @@
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateMachine wifiStateMachine) {
- mWifiStateMachine = wifiStateMachine;
+ public WifiMonitor(StateMachine wifiStateMachine) {
+ mStateMachine = wifiStateMachine;
}
public void startMonitoring() {
@@ -170,9 +278,9 @@
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateMachine.notifySupplicantConnection();
+ mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
} else {
- mWifiStateMachine.notifySupplicantLost();
+ mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
return;
}
@@ -181,20 +289,24 @@
String eventStr = WifiNative.waitForEvent();
// Skip logging the common but mostly uninteresting scan-results event
- if (false && eventStr.indexOf(scanResultsEvent) == -1) {
- Log.v(TAG, "Event [" + eventStr + "]");
+ if (false && eventStr.indexOf(SCAN_RESULTS_STR) == -1) {
+ Log.d(TAG, "Event [" + eventStr + "]");
}
- if (!eventStr.startsWith(eventPrefix)) {
- if (eventStr.startsWith(wpaEventPrefix) &&
- 0 < eventStr.indexOf(passwordKeyMayBeIncorrectEvent)) {
- mWifiStateMachine.notifyAuthenticationFailure();
- } else if (eventStr.startsWith(wpsOverlapEvent)) {
- mWifiStateMachine.notifyWpsOverlap();
+ if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
+ if (eventStr.startsWith(WPA_EVENT_PREFIX_STR) &&
+ 0 < eventStr.indexOf(PASSWORD_MAY_BE_INCORRECT_STR)) {
+ mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+ } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
+ mStateMachine.sendMessage(WPS_OVERLAP_EVENT);
+ } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
+ handleP2pEvents(eventStr);
+ } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
+ handleHostApEvents(eventStr);
}
continue;
}
- String eventName = eventStr.substring(eventPrefixLen);
+ String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
int nameEnd = eventName.indexOf(' ');
if (nameEnd != -1)
eventName = eventName.substring(0, nameEnd);
@@ -206,21 +318,21 @@
* Map event name into event enum
*/
int event;
- if (eventName.equals(connectedEvent))
+ if (eventName.equals(CONNECTED_STR))
event = CONNECTED;
- else if (eventName.equals(disconnectedEvent))
+ else if (eventName.equals(DISCONNECTED_STR))
event = DISCONNECTED;
- else if (eventName.equals(stateChangeEvent))
+ else if (eventName.equals(STATE_CHANGE_STR))
event = STATE_CHANGE;
- else if (eventName.equals(scanResultsEvent))
+ else if (eventName.equals(SCAN_RESULTS_STR))
event = SCAN_RESULTS;
- else if (eventName.equals(linkSpeedEvent))
+ else if (eventName.equals(LINK_SPEED_STR))
event = LINK_SPEED;
- else if (eventName.equals(terminatingEvent))
+ else if (eventName.equals(TERMINATING_STR))
event = TERMINATING;
- else if (eventName.equals(driverStateEvent))
+ else if (eventName.equals(DRIVER_STATE_STR))
event = DRIVER_STATE;
- else if (eventName.equals(eapFailureEvent))
+ else if (eventName.equals(EAP_FAILURE_STR))
event = EAP_FAILURE;
else
event = UNKNOWN;
@@ -249,7 +361,7 @@
* If monitor socket is closed, we have already
* stopped the supplicant, simply exit the monitor thread
*/
- if (eventData.startsWith(monitorSocketClosed)) {
+ if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
if (false) {
Log.d(TAG, "Monitor socket is closed, exiting thread");
}
@@ -260,7 +372,7 @@
* Close the supplicant connection if we see
* too many recv errors
*/
- if (eventData.startsWith(wpaRecvError)) {
+ if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
if (++mRecvErrors > MAX_RECV_ERRORS) {
if (false) {
Log.d(TAG, "too many recv errors, closing connection");
@@ -271,11 +383,11 @@
}
// notify and exit
- mWifiStateMachine.notifySupplicantLost();
+ mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
break;
} else if (event == EAP_FAILURE) {
- if (eventData.startsWith(eapAuthFailure)) {
- mWifiStateMachine.notifyAuthenticationFailure();
+ if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
+ mStateMachine.sendMessage(AUTHENTICATION_FAILURE_EVENT);
}
} else {
handleEvent(event, eventData);
@@ -305,7 +417,7 @@
return;
}
if (state.equals("HANGED")) {
- mWifiStateMachine.notifyDriverHung();
+ mStateMachine.sendMessage(DRIVER_HUNG_EVENT);
}
}
@@ -326,7 +438,7 @@
break;
case SCAN_RESULTS:
- mWifiStateMachine.notifyScanResultsAvailable();
+ mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
break;
case UNKNOWN:
@@ -335,6 +447,59 @@
}
/**
+ * Handle p2p events
+ */
+ private void handleP2pEvents(String dataString) {
+ if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
+ mStateMachine.sendMessage(P2P_DEVICE_FOUND_EVENT, new WifiP2pDevice(dataString));
+ } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
+ mStateMachine.sendMessage(P2P_DEVICE_LOST_EVENT, new WifiP2pDevice(dataString));
+ } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
+ mStateMachine.sendMessage(P2P_GO_NEGOTIATION_REQUEST_EVENT,
+ new WifiP2pConfig(dataString));
+ } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
+ mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
+ } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
+ mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT);
+ } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
+ mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
+ } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
+ mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT);
+ } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
+ mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
+ } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
+ mStateMachine.sendMessage(P2P_GROUP_REMOVED_EVENT, new WifiP2pGroup(dataString));
+ } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
+ mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
+ new WifiP2pGroup(dataString));
+ } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
+ String[] tokens = dataString.split(" ");
+ if (tokens.length != 2) return;
+ String[] nameValue = tokens[1].split("=");
+ if (nameValue.length != 2) return;
+ mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, nameValue[1]);
+ } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
+ mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
+ new WifiP2pDevice(dataString));
+ } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
+ mStateMachine.sendMessage(P2P_PROV_DISC_ENTER_PIN_EVENT,
+ new WifiP2pDevice(dataString));
+ }
+ }
+
+ /**
+ * Handle hostap events
+ */
+ private void handleHostApEvents(String dataString) {
+ String[] tokens = dataString.split(" ");
+ if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
+ mStateMachine.sendMessage(AP_STA_CONNECTED_EVENT, tokens[1]);
+ } else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
+ mStateMachine.sendMessage(AP_STA_DISCONNECTED_EVENT, tokens[1]);
+ }
+ }
+
+ /**
* Handle the supplicant STATE-CHANGE event
* @param dataString New supplicant state string in the format:
* id=network-id state=new-state
@@ -383,7 +548,7 @@
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+ notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -403,7 +568,40 @@
}
}
}
- mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
+ notifyNetworkStateChange(newState, BSSID, networkId);
+ }
+
+ /**
+ * Send the state machine a notification that the state of Wifi connectivity
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyNetworkStateChange(NetworkInfo.DetailedState newState, String BSSID, int netId) {
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
+ netId, 0, BSSID);
+ mStateMachine.sendMessage(m);
+ } else {
+ Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
+ netId, 0, BSSID);
+ mStateMachine.sendMessage(m);
+ }
+ }
+
+ /**
+ * Send the state machine a notification that the state of the supplicant
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ mStateMachine.sendMessage(mStateMachine.obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
}
/**
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index f1f0fcc..3b043b3 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -16,6 +16,16 @@
package android.net.wifi;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.lang.Process;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Native calls for sending requests to the supplicant daemon, and for
* receiving asynchronous events. All methods of the form "xxxxCommand()"
@@ -28,6 +38,10 @@
* WifiStateTracker class except for waitForEvent() call which is
* on a separate monitor channel for WifiMonitor
*
+ * TODO: clean up the API and move the functionality from JNI to here. We should
+ * be able to get everything done with doBooleanCommand, doIntCommand and
+ * doStringCommand native commands
+ *
* {@hide}
*/
public class WifiNative {
@@ -186,4 +200,141 @@
public native static void enableBackgroundScanCommand(boolean enable);
public native static void setScanIntervalCommand(int scanInterval);
+
+ private native static boolean doBooleanCommand(String command);
+
+ //STOPSHIP: remove this after native interface works and replace all
+ //calls to doBooleanTempCommand() with doBooleanCommand()
+ private static boolean doBooleanTempCommand(String command) {
+ try {
+ String str = "/system/bin/wpa_cli " + command;
+ Log.e("WifiNative", "===> " + str);
+ Runtime.getRuntime()
+ .exec(str).waitFor();
+ } catch (Exception e) {
+ Log.e("WifiNative", "exception with doBooleanTempCommand");
+ return false;
+ }
+ return true;
+ }
+
+ private static String doStringTempCommand(String command) {
+ String lines[] = null;
+ try {
+ String str = "/system/bin/wpa_cli " + command;
+ Log.e("WifiNative", "===> " + str);
+ Process p = Runtime.getRuntime()
+ .exec(str);
+ InputStream in = p.getInputStream();
+ p.waitFor();
+ byte[] bytes=new byte[in.available()];
+ in.read(bytes);
+ String s = new String(bytes);
+ Log.e("WifiNative", "====> doString: " + s);
+ lines = s.split("\\r?\\n");
+ } catch (Exception e) {
+ Log.e("WifiNative", "exception with doBooleanTempCommand");
+ return null;
+ }
+ return lines[1];
+ }
+
+ private native static int doIntCommand(String command);
+
+ private native static String doStringCommand(String command);
+
+ public static boolean p2pFind() {
+ return doBooleanTempCommand("p2p_find");
+ }
+
+ public static boolean p2pFind(int timeout) {
+ if (timeout <= 0) {
+ return p2pFind();
+ }
+ return doBooleanTempCommand("p2p_find " + timeout);
+ }
+
+ public static boolean p2pListen() {
+ return doBooleanTempCommand("p2p_listen");
+ }
+
+ public static boolean p2pListen(int timeout) {
+ if (timeout <= 0) {
+ return p2pListen();
+ }
+ return doBooleanTempCommand("p2p_listen " + timeout);
+ }
+
+ public static boolean p2pFlush() {
+ return doBooleanTempCommand("p2p_flush");
+ }
+
+ /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
+ [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
+ public static String p2pConnect(WifiP2pConfig config) {
+ if (config == null) return null;
+ List<String> args = new ArrayList<String>();
+ WpsConfiguration wpsConfig = config.wpsConfig;
+ args.add(config.deviceAddress);
+
+ switch (wpsConfig.setup) {
+ case PBC:
+ args.add("pbc");
+ break;
+ case DISPLAY:
+ //TODO: pass the pin back for display
+ args.add("pin");
+ args.add("display");
+ break;
+ case KEYPAD:
+ args.add(wpsConfig.pin);
+ args.add("keypad");
+ break;
+ case LABEL:
+ args.add(wpsConfig.pin);
+ args.add("label");
+ default:
+ break;
+ }
+
+ if (config.isPersistent) args.add("persistent");
+ if (config.joinExistingGroup) args.add("join");
+
+ args.add("go_intent=" + config.groupOwnerIntent);
+ if (config.channel > 0) args.add("freq=" + config.channel);
+
+ String command = "p2p_connect ";
+ for (String s : args) command += s + " ";
+
+ return doStringTempCommand(command);
+ }
+
+ public static boolean p2pGroupAdd() {
+ return doBooleanTempCommand("p2p_group_add");
+ }
+
+ public static boolean p2pGroupRemove(String iface) {
+ if (iface == null) return false;
+ return doBooleanTempCommand("p2p_group_remove " + iface);
+ }
+
+ public static boolean p2pReject(String deviceAddress) {
+ return doBooleanTempCommand("p2p_reject " + deviceAddress);
+ }
+
+ /* Invite a peer to a group */
+ public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
+ if (group == null || deviceAddress == null) return false;
+ return doBooleanTempCommand("p2p_invite group=" + group.getInterface()
+ + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
+ }
+
+ public static boolean p2pWpsPbc() {
+ return doBooleanTempCommand("wps_pbc");
+ }
+
+ public static boolean p2pWpsPin(String pin) {
+ return doBooleanTempCommand("wps_pin any " + pin);
+ }
+
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index f08bb6a..74c6b49 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -56,6 +56,8 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkUtils;
import android.net.wifi.WpsResult.Status;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.StateChangeResult;
import android.os.Binder;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -67,6 +69,7 @@
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.WorkSource;
+import android.server.WifiP2pService;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
@@ -89,6 +92,14 @@
* Track the state of Wifi connectivity. All event handling is done here,
* and all changes in connectivity state are initiated here.
*
+ * Wi-Fi now supports three modes of operation: Client, Soft Ap and Direct
+ * In the current implementation, we do not support any concurrency and thus only
+ * one of Client, Soft Ap or Direct operation is supported at any time.
+ *
+ * The WifiStateMachine supports Soft Ap and Client operations while WifiP2pService
+ * handles Direct. WifiP2pService and WifiStateMachine co-ordinate to ensure only
+ * one exists at a certain time.
+ *
* @hide
*/
public class WifiStateMachine extends StateMachine {
@@ -97,7 +108,7 @@
private static final String NETWORKTYPE = "WIFI";
private static final boolean DBG = false;
- /* TODO: fetch a configurable interface */
+ /* TODO: This is no more used with the hostapd code. Clean up */
private static final String SOFTAP_IFACE = "wl0.1";
private WifiMonitor mWifiMonitor;
@@ -167,6 +178,10 @@
// Channel for sending replies.
private AsyncChannel mReplyChannel = new AsyncChannel();
+ private WifiP2pManager mWifiP2pManager;
+ //Used to initiate a connection with WifiP2pService
+ private AsyncChannel mWifiP2pChannel = new AsyncChannel();
+
// Event log tags (must be in sync with event-log-tags)
private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
@@ -208,27 +223,10 @@
static final int CMD_SET_AP_CONFIG = BASE + 23;
/* Get the soft access point configuration */
static final int CMD_GET_AP_CONFIG = BASE + 24;
+ /* Set configuration on tether interface */
+ static final int CMD_TETHER_INTERFACE = BASE + 25;
- static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 25;
-
- /* Supplicant events */
- /* Connection to supplicant established */
- static final int SUP_CONNECTION_EVENT = BASE + 31;
- /* Connection to supplicant lost */
- static final int SUP_DISCONNECTION_EVENT = BASE + 32;
- /* Network connection completed */
- static final int NETWORK_CONNECTION_EVENT = BASE + 33;
- /* Network disconnection completed */
- static final int NETWORK_DISCONNECTION_EVENT = BASE + 34;
- /* Scan results are available */
- static final int SCAN_RESULTS_EVENT = BASE + 35;
- /* Supplicate state changed */
- static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 36;
- /* Password failure and EAP authentication failure */
- static final int AUTHENTICATION_FAILURE_EVENT = BASE + 37;
- /* WPS overlap detected */
- static final int WPS_OVERLAP_EVENT = BASE + 38;
-
+ static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 26;
/* Supplicant commands */
/* Is supplicant alive ? */
@@ -330,6 +328,10 @@
/* Reset the WPS state machine */
static final int CMD_RESET_WPS_STATE = BASE + 122;
+ /* Interaction with WifiP2pService */
+ public static final int WIFI_ENABLE_PENDING = BASE + 131;
+ public static final int P2P_ENABLE_PROCEED = BASE + 132;
+
private static final int CONNECT_MODE = 1;
private static final int SCAN_ONLY_MODE = 2;
@@ -421,8 +423,13 @@
/* Waiting for WPS to be completed*/
private State mWaitForWpsCompletionState = new WaitForWpsCompletionState();
- /* Soft Ap is running */
+ /* Soft ap is running */
private State mSoftApStartedState = new SoftApStartedState();
+ /* Soft ap is running and we are tethered through connectivity service */
+ private State mTetheredState = new TetheredState();
+
+ /* Wait till p2p is disabled */
+ private State mWaitForP2pDisableState = new WaitForP2pDisableState();
/**
@@ -513,13 +520,9 @@
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
-
ArrayList<String> available = intent.getStringArrayListExtra(
ConnectivityManager.EXTRA_AVAILABLE_TETHER);
- ArrayList<String> active = intent.getStringArrayListExtra(
- ConnectivityManager.EXTRA_ACTIVE_TETHER);
- updateTetherState(available, active);
-
+ sendMessage(CMD_TETHER_INTERFACE, available);
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
@@ -559,6 +562,8 @@
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApStartedState, mDefaultState);
+ addState(mTetheredState, mSoftApStartedState);
+ addState(mWaitForP2pDisableState, mDefaultState);
setInitialState(mInitialState);
@@ -1048,14 +1053,17 @@
* Internal private functions
********************************************************/
- private void updateTetherState(ArrayList<String> available, ArrayList<String> tethered) {
-
- boolean wifiTethered = false;
- boolean wifiAvailable = false;
-
+ private void checkAndSetConnectivityInstance() {
if (mCm == null) {
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ }
+
+ private boolean startTethering(ArrayList<String> available) {
+
+ boolean wifiAvailable = false;
+
+ checkAndSetConnectivityInstance();
String[] wifiRegexs = mCm.getTetherableWifiRegexs();
@@ -1077,18 +1085,43 @@
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + intf + ", :" + e);
setWifiApEnabled(null, false);
- return;
+ return false;
}
if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
Log.e(TAG, "Error tethering on " + intf);
setWifiApEnabled(null, false);
- return;
+ return false;
}
- break;
+ return true;
}
}
}
+ // We found no interfaces to tether
+ return false;
+ }
+
+ private void stopTethering() {
+
+ checkAndSetConnectivityInstance();
+
+ /* Clear the interface config to allow dhcp correctly configure new
+ ip settings */
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = nwService.getInterfaceConfig(mInterfaceName);
+ if (ifcg != null) {
+ ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
+ "0.0.0.0"), 0);
+ nwService.setInterfaceConfig(mInterfaceName, ifcg);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error resetting interface " + mInterfaceName + ", :" + e);
+ }
+
+ if (mCm.untether(mInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Untether initiate failed!");
+ }
}
/**
@@ -1616,12 +1649,15 @@
if (currentStatus == SOFT_AP_STOPPED) {
nwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
} else if (currentStatus == SOFT_AP_RUNNING) {
- nwService.setAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
+ //nwService.setAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
+ //TODO: when we have a control channel to hostapd, we should not need to do this
+ nwService.stopAccessPoint(mInterfaceName);
+ nwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
}
} catch (Exception e) {
Log.e(TAG, "Exception in softap start " + e);
try {
- nwService.stopAccessPoint();
+ nwService.stopAccessPoint(mInterfaceName);
nwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
} catch (Exception ee) {
Log.e(TAG, "Exception during softap restart : " + ee);
@@ -1631,105 +1667,6 @@
return true;
}
-
- /*********************************************************
- * Notifications from WifiMonitor
- ********************************************************/
-
- /**
- * Stores supplicant state change information passed from WifiMonitor
- */
- static class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, SupplicantState state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- SupplicantState state;
- }
-
- /**
- * Send the tracker a notification that a user provided
- * configuration caused authentication failure - this could
- * be a password failure or a EAP authentication failure
- */
- void notifyAuthenticationFailure() {
- sendMessage(AUTHENTICATION_FAILURE_EVENT);
- }
-
- /**
- * Send a notification that the supplicant has detected overlapped
- * WPS sessions
- */
- void notifyWpsOverlap() {
- sendMessage(WPS_OVERLAP_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendMessage(SUP_CONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that connection to the supplicant
- * daemon is lost
- */
- void notifySupplicantLost() {
- sendMessage(SUP_DISCONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that the state of Wifi connectivity
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new network state
- * @param BSSID when the new state is {@link DetailedState#CONNECTED
- * NetworkInfo.DetailedState.CONNECTED},
- * this is the MAC address of the access point. Otherwise, it
- * is {@code null}.
- */
- void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
- if (newState == NetworkInfo.DetailedState.CONNECTED) {
- sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT, networkId, 0, BSSID));
- } else {
- sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, networkId, 0, BSSID));
- }
- }
-
- /**
- * Send the tracker a notification that the state of the supplicant
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new {@code SupplicantState}
- */
- void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
- sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- /**
- * Switch scan mode over to passive.
- * Turning off scan-only mode happens only in "Connect" mode
- */
- setScanType(false);
- sendMessage(SCAN_RESULTS_EVENT);
- }
-
- void notifyDriverHung() {
- setWifiEnabled(false);
- setWifiEnabled(true);
- }
-
/********************************************************
* HSM states
*******************************************************/
@@ -1739,6 +1676,18 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mWifiP2pChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ } else {
+ Log.e(TAG, "WifiP2pService connection failure, error=" + message.arg1);
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ Log.e(TAG, "WifiP2pService channel lost, message.arg1 =" + message.arg1);
+ //TODO: Re-establish connection to state machine after a delay
+ //mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
+ break;
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive = (message.arg1 !=
BluetoothAdapter.STATE_DISCONNECTED);
@@ -1774,18 +1723,19 @@
case CMD_STOP_DRIVER:
case CMD_START_AP:
case CMD_STOP_AP:
+ case CMD_TETHER_INTERFACE:
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case SUP_CONNECTION_EVENT:
- case SUP_DISCONNECTION_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- case SCAN_RESULTS_EVENT:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case AUTHENTICATION_FAILURE_EVENT:
- case WPS_OVERLAP_EVENT:
+ case WifiMonitor.SUP_CONNECTION_EVENT:
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ case WifiMonitor.WPS_OVERLAP_EVENT:
case CMD_BLACKLIST_NETWORK:
case CMD_CLEAR_BLACKLIST:
case CMD_SET_SCAN_MODE:
@@ -1802,11 +1752,20 @@
case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
break;
+ case WifiMonitor.DRIVER_HUNG_EVENT:
+ setWifiEnabled(false);
+ setWifiEnabled(true);
+ break;
case CMD_START_WPS:
/* Return failure when the state machine cannot handle WPS initiation*/
mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
new WpsResult(Status.FAILURE));
break;
+ case WifiP2pService.P2P_ENABLE_PENDING:
+ // turn off wifi and defer to be handled in DriverUnloadedState
+ setWifiEnabled(false);
+ deferMessage(message);
+ break;
default:
Log.e(TAG, "Error! unhandled message" + message);
break;
@@ -1833,6 +1792,11 @@
else {
transitionTo(mDriverUnloadedState);
}
+
+ //Connect to WifiP2pService
+ mWifiP2pManager = (WifiP2pManager) mContext.getSystemService(Context.WIFI_P2P_SERVICE);
+ mWifiP2pChannel.connect(mContext, getHandler(), mWifiP2pManager.getMessenger());
+
}
}
@@ -2048,7 +2012,11 @@
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
case CMD_LOAD_DRIVER:
- transitionTo(mDriverLoadingState);
+ mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
+ transitionTo(mWaitForP2pDisableState);
+ break;
+ case WifiP2pService.P2P_ENABLE_PENDING:
+ mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
break;
default:
return NOT_HANDLED;
@@ -2082,7 +2050,7 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case SUP_CONNECTION_EVENT:
+ case WifiMonitor.SUP_CONNECTION_EVENT:
Log.d(TAG, "Supplicant connection established");
setWifiState(WIFI_STATE_ENABLED);
mSupplicantRestartCount = 0;
@@ -2102,7 +2070,7 @@
sendSupplicantConnectionChangedBroadcast(true);
transitionTo(mDriverStartedState);
break;
- case SUP_DISCONNECTION_EVENT:
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
Log.e(TAG, "Failed to setup control channel, restart supplicant");
WifiNative.killSupplicant();
@@ -2165,7 +2133,7 @@
case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
transitionTo(mSupplicantStoppingState);
break;
- case SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
+ case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
Log.e(TAG, "Connection lost, restart supplicant");
WifiNative.killSupplicant();
WifiNative.closeSupplicantConnection();
@@ -2177,7 +2145,7 @@
transitionTo(mDriverLoadedState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
break;
- case SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
eventLoggingEnabled = false;
setScanResults(WifiNative.scanResultsCommand());
sendScanResultsAvailableBroadcast();
@@ -2279,10 +2247,10 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case SUP_CONNECTION_EVENT:
+ case WifiMonitor.SUP_CONNECTION_EVENT:
Log.e(TAG, "Supplicant connection received while stopping");
break;
- case SUP_DISCONNECTION_EVENT:
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
Log.d(TAG, "Supplicant connection lost");
WifiNative.closeSupplicantConnection();
transitionTo(mDriverLoadedState);
@@ -2322,7 +2290,7 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
/* If suplicant is exiting out of INTERFACE_DISABLED state into
* a state that indicates driver has started, it is ready to
@@ -2335,10 +2303,10 @@
/* Queue driver commands & connection events */
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- case AUTHENTICATION_FAILURE_EVENT:
- case WPS_OVERLAP_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ case WifiMonitor.WPS_OVERLAP_EVENT:
case CMD_SET_SCAN_TYPE:
case CMD_SET_HIGH_PERF_MODE:
case CMD_SET_COUNTRY_CODE:
@@ -2495,7 +2463,7 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
if (state == SupplicantState.INTERFACE_DISABLED) {
transitionTo(mDriverStoppedState);
@@ -2539,7 +2507,7 @@
WifiNative.startDriverCommand();
mWakeLock.release();
break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
/* A driver start causes supplicant to first report an INTERFACE_DISABLED
* state before transitioning out of it for connection. Stay in
@@ -2585,9 +2553,9 @@
case CMD_DISCONNECT:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
break;
default:
return NOT_HANDLED;
@@ -2608,14 +2576,14 @@
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
StateChangeResult stateChangeResult;
switch(message.what) {
- case AUTHENTICATION_FAILURE_EVENT:
- mSupplicantStateTracker.sendMessage(AUTHENTICATION_FAILURE_EVENT);
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
break;
- case WPS_OVERLAP_EVENT:
+ case WifiMonitor.WPS_OVERLAP_EVENT:
/* We just need to broadcast the error */
sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
handleSupplicantStateChange(message);
break;
/* Do a redundant disconnect without transition */
@@ -2657,12 +2625,12 @@
mWpsStateMachine.sendMessage(Message.obtain(message));
transitionTo(mWaitForWpsCompletionState);
break;
- case SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
/* Set the scan setting back to "connect" mode */
WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
/* Handle scan results */
return NOT_HANDLED;
- case NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
Log.d(TAG,"Network connection established");
mLastNetworkId = message.arg1;
mLastBssid = (String) message.obj;
@@ -2676,7 +2644,7 @@
sendNetworkStateChangeBroadcast(mLastBssid);
transitionTo(mConnectingState);
break;
- case NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
Log.d(TAG,"Network connection lost");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
@@ -2763,7 +2731,7 @@
deferMessage(message);
break;
/* Ignore */
- case NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
break;
case CMD_STOP_DRIVER:
sendMessage(CMD_DISCONNECT);
@@ -2828,10 +2796,7 @@
deferMessage(message);
break;
case CMD_REQUEST_CM_WAKELOCK:
- if (mCm == null) {
- mCm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
+ checkAndSetConnectivityInstance();
mCm.requestNetworkTransitionWakelock(TAG);
break;
case CMD_SET_SCAN_MODE:
@@ -2873,7 +2838,7 @@
}
break;
/* Ignore */
- case NETWORK_CONNECTION_EVENT:
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
break;
case CMD_RSSI_POLL:
eventLoggingEnabled = false;
@@ -2934,7 +2899,7 @@
}
break;
/* Handle in DisconnectedState */
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
deferMessage(message);
break;
default:
@@ -3019,9 +2984,9 @@
}
break;
/* Ignore network disconnect */
- case NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
/* ConnectModeState does the rest of the handling */
@@ -3033,7 +2998,7 @@
}
/* Handled in parent state */
return NOT_HANDLED;
- case SCAN_RESULTS_EVENT:
+ case WifiMonitor.SCAN_RESULTS_EVENT:
/* Re-enable background scan when a pending scan result is received */
if (mEnableBackgroundScan && mScanResultIsPending) {
WifiNative.enableBackgroundScanCommand(true);
@@ -3076,10 +3041,10 @@
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
+ case WifiMonitor.NETWORK_CONNECTION_EVENT: /* Handled after IP & proxy update */
deferMessage(message);
break;
- case NETWORK_DISCONNECTION_EVENT:
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
Log.d(TAG,"Network connection lost");
handleNetworkDisconnect();
break;
@@ -3109,16 +3074,9 @@
case CMD_STOP_AP:
Log.d(TAG,"Stopping Soft AP");
setWifiApState(WIFI_AP_STATE_DISABLING);
-
- if (mCm == null) {
- mCm = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
- if (mCm.untether(SOFTAP_IFACE) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
- Log.e(TAG, "Untether initiate failed!");
- }
+ stopTethering();
try {
- nwService.stopAccessPoint();
+ nwService.stopAccessPoint(mInterfaceName);
} catch(Exception e) {
Log.e(TAG, "Exception in stopAccessPoint()");
}
@@ -3140,6 +3098,17 @@
Log.e(TAG,"Cannot start supplicant with a running soft AP");
setWifiState(WIFI_STATE_UNKNOWN);
break;
+ case CMD_TETHER_INTERFACE:
+ ArrayList<String> available = (ArrayList<String>) message.obj;
+ if (startTethering(available)) {
+ transitionTo(mTetheredState);
+ }
+ break;
+ case WifiP2pService.P2P_ENABLE_PENDING:
+ // turn of soft Ap and defer to be handled in DriverUnloadedState
+ setWifiApEnabled(null, false);
+ deferMessage(message);
+ break;
default:
return NOT_HANDLED;
}
@@ -3147,4 +3116,68 @@
return HANDLED;
}
}
+
+ class WaitForP2pDisableState extends State {
+ private int mSavedArg;
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ //Preserve the argument arg1 that has information used in DriverLoadingState
+ mSavedArg = getCurrentMessage().arg1;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case WifiP2pService.WIFI_ENABLE_PROCEED:
+ //restore argument from original message (CMD_LOAD_DRIVER)
+ message.arg1 = mSavedArg;
+ transitionTo(mDriverLoadingState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_HIGH_PERF_MODE:
+ case CMD_SET_COUNTRY_CODE:
+ case CMD_SET_FREQUENCY_BAND:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class TetheredState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_TETHER_INTERFACE:
+ // Ignore any duplicate interface available notifications
+ // when in tethered state
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/WpsConfiguration.java b/wifi/java/android/net/wifi/WpsConfiguration.java
index 12d951f..2e7689a 100644
--- a/wifi/java/android/net/wifi/WpsConfiguration.java
+++ b/wifi/java/android/net/wifi/WpsConfiguration.java
@@ -30,13 +30,16 @@
*/
public class WpsConfiguration implements Parcelable {
+ /* Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
public enum Setup {
- /* Wi-Fi protected setup push button configuration */
+ /* Push button configuration */
PBC,
- /* Wi-Fi protected setup pin method configuration with pin obtained from access point */
- PIN_FROM_ACCESS_POINT,
- /* Wi-Fi protected setup pin method configuration with pin obtained from device */
- PIN_FROM_DEVICE,
+ /* Display pin method configuration - pin is generated and displayed on device */
+ DISPLAY,
+ /* Keypad pin method configuration - pin is entered on device */
+ KEYPAD,
+ /* Label pin method configuration - pin is obtained from a printed label */
+ LABEL,
/* Invalid config */
INVALID
}
diff --git a/wifi/java/android/net/wifi/WpsStateMachine.java b/wifi/java/android/net/wifi/WpsStateMachine.java
index 120b228..af089ab 100644
--- a/wifi/java/android/net/wifi/WpsStateMachine.java
+++ b/wifi/java/android/net/wifi/WpsStateMachine.java
@@ -22,7 +22,7 @@
import android.content.Context;
import android.content.Intent;
-import android.net.wifi.WifiStateMachine.StateChangeResult;
+import android.net.wifi.StateChangeResult;
import android.net.wifi.WpsResult.Status;
import android.os.Handler;
import android.os.Message;
@@ -99,10 +99,10 @@
case PBC:
result = WifiConfigStore.startWpsPbc(mWpsConfig);
break;
- case PIN_FROM_ACCESS_POINT:
+ case KEYPAD:
result = WifiConfigStore.startWpsWithPinFromAccessPoint(mWpsConfig);
break;
- case PIN_FROM_DEVICE:
+ case DISPLAY:
result = WifiConfigStore.startWpsWithPinFromDevice(mWpsConfig);
break;
default:
@@ -139,7 +139,7 @@
boolean retValue = HANDLED;
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
- case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState supState = (SupplicantState) stateChangeResult.state;
switch (supState) {
@@ -194,7 +194,7 @@
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
//Ignore supplicant state changes
- case WifiStateMachine.SUPPLICANT_STATE_CHANGE_EVENT:
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
break;
default:
retValue = NOT_HANDLED;
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
new file mode 100644
index 0000000..a0c7dd1
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Messenger;
+
+/**
+ * Interface that WifiP2pService implements
+ *
+ * {@hide}
+ */
+interface IWifiP2pManager
+{
+ Messenger getMessenger();
+ boolean isP2pSupported();
+}
+
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl
new file mode 100644
index 0000000..ea3b280
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pConfig;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
new file mode 100644
index 0000000..fff5ee3
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.net.wifi.WpsConfiguration;
+import android.net.wifi.WpsConfiguration.Setup;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A class representing a Wi-Fi P2p configuration
+ * @hide
+ */
+public class WifiP2pConfig implements Parcelable {
+
+ /**
+ * Device name
+ */
+ public String deviceName;
+
+ /**
+ * Device address
+ */
+ public String deviceAddress;
+
+ /**
+ * WPS configuration
+ */
+ public WpsConfiguration wpsConfig;
+
+ /**
+ * This is an integer value between 0 and 15 where 0 indicates the least
+ * inclination to be a group owner and 15 indicates the highest inclination
+ * to be a group owner.
+ */
+ public int groupOwnerIntent;
+
+ public boolean isPersistent;
+
+ public boolean joinExistingGroup;
+
+ /**
+ * Channel frequency in MHz
+ */
+ public int channel;
+
+ public WifiP2pConfig() {
+ //set defaults
+ wpsConfig = new WpsConfiguration();
+ wpsConfig.setup = Setup.PBC;
+ }
+
+ /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
+ public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
+ String[] tokens = supplicantEvent.split(" ");
+
+ if (tokens.length < 2 || !tokens[0].equals("P2P-GO-NEG-REQUEST")) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+
+ deviceAddress = tokens[1];
+ wpsConfig = new WpsConfiguration();
+
+ if (tokens.length > 2) {
+ String[] nameVal = tokens[2].split("=");
+ int devPasswdId;
+ try {
+ devPasswdId = Integer.parseInt(nameVal[1]);
+ } catch (NumberFormatException e) {
+ devPasswdId = 0;
+ }
+ //As defined in wps/wps_defs.h
+ switch (devPasswdId) {
+ case 0x00:
+ wpsConfig.setup = Setup.LABEL;
+ break;
+ case 0x01:
+ wpsConfig.setup = Setup.KEYPAD;
+ break;
+ case 0x04:
+ wpsConfig.setup = Setup.PBC;
+ break;
+ case 0x05:
+ wpsConfig.setup = Setup.DISPLAY;
+ break;
+ default:
+ wpsConfig.setup = Setup.PBC;
+ break;
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("Device: ").append(deviceName);
+ sbuf.append("\n address: ").append(deviceAddress);
+ sbuf.append("\n wps: ").append(wpsConfig);
+ sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
+ sbuf.append("\n isPersistent: ").append(isPersistent);
+ sbuf.append("\n joinExistingGroup: ").append(joinExistingGroup);
+ sbuf.append("\n channel: ").append(channel);
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ public WifiP2pConfig(WifiP2pConfig source) {
+ if (source != null) {
+ //TODO: implement
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(deviceName);
+ dest.writeString(deviceAddress);
+ dest.writeParcelable(wpsConfig, flags);
+ dest.writeInt(groupOwnerIntent);
+ dest.writeInt(isPersistent ? 1 : 0);
+ dest.writeInt(joinExistingGroup ? 1 : 0);
+ dest.writeInt(channel);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pConfig> CREATOR =
+ new Creator<WifiP2pConfig>() {
+ public WifiP2pConfig createFromParcel(Parcel in) {
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceName = in.readString();
+ config.deviceAddress = in.readString();
+ config.wpsConfig = (WpsConfiguration) in.readParcelable(null);
+ config.groupOwnerIntent = in.readInt();
+ config.isPersistent = (in.readInt() == 1);
+ config.joinExistingGroup = (in.readInt() == 1);
+ config.channel = in.readInt();
+ return config;
+ }
+
+ public WifiP2pConfig[] newArray(int size) {
+ return new WifiP2pConfig[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl
new file mode 100644
index 0000000..8790c6f
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pDevice;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
new file mode 100644
index 0000000..83dc285
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.regex.Pattern;
+
+/**
+ * A class representing a Wi-Fi p2p device
+ * @hide
+ */
+public class WifiP2pDevice implements Parcelable {
+
+ private static final String TAG = "WifiP2pDevice";
+ /**
+ * Device name
+ */
+ public String deviceName;
+
+ /**
+ * Device MAC address
+ */
+ public String deviceAddress;
+
+ /**
+ * interfaceAddress
+ *
+ * This address is used during group owner negotiation as the Intended
+ * P2P Interface Address and the group interface will be created with
+ * address as the local address in case of successfully completed
+ * negotiation.
+ */
+ public String interfaceAddress;
+
+ /**
+ * Primary device type
+ */
+ public String primaryDeviceType;
+
+ /**
+ * Secondary device type
+ */
+ public String secondaryDeviceType;
+
+
+ // These definitions match the ones in wpa_supplicant
+ /* WPS config methods supported */
+ private static final int WPS_CONFIG_USBA = 0x0001;
+ private static final int WPS_CONFIG_ETHERNET = 0x0002;
+ private static final int WPS_CONFIG_LABEL = 0x0004;
+ private static final int WPS_CONFIG_DISPLAY = 0x0008;
+ private static final int WPS_CONFIG_EXT_NFC_TOKEN = 0x0010;
+ private static final int WPS_CONFIG_INT_NFC_TOKEN = 0x0020;
+ private static final int WPS_CONFIG_NFC_INTERFACE = 0x0040;
+ private static final int WPS_CONFIG_PUSHBUTTON = 0x0080;
+ private static final int WPS_CONFIG_KEYPAD = 0x0100;
+ private static final int WPS_CONFIG_VIRT_PUSHBUTTON = 0x0280;
+ private static final int WPS_CONFIG_PHY_PUSHBUTTON = 0x0480;
+ private static final int WPS_CONFIG_VIRT_DISPLAY = 0x2008;
+ private static final int WPS_CONFIG_PHY_DISPLAY = 0x4008;
+
+ /* Device Capability bitmap */
+ private static final int DEVICE_CAPAB_SERVICE_DISCOVERY = 1;
+ private static final int DEVICE_CAPAB_CLIENT_DISCOVERABILITY = 1<<1;
+ private static final int DEVICE_CAPAB_CONCURRENT_OPER = 1<<2;
+ private static final int DEVICE_CAPAB_INFRA_MANAGED = 1<<3;
+ private static final int DEVICE_CAPAB_DEVICE_LIMIT = 1<<4;
+ private static final int DEVICE_CAPAB_INVITATION_PROCEDURE = 1<<5;
+
+ /* Group Capability bitmap */
+ private static final int GROUP_CAPAB_GROUP_OWNER = 1;
+ private static final int GROUP_CAPAB_PERSISTENT_GROUP = 1<<1;
+ private static final int GROUP_CAPAB_GROUP_LIMIT = 1<<2;
+ private static final int GROUP_CAPAB_INTRA_BSS_DIST = 1<<3;
+ private static final int GROUP_CAPAB_CROSS_CONN = 1<<4;
+ private static final int GROUP_CAPAB_PERSISTENT_RECONN = 1<<5;
+ private static final int GROUP_CAPAB_GROUP_FORMATION = 1<<6;
+
+ /**
+ * WPS config methods supported
+ */
+ public int wpsConfigMethodsSupported;
+
+ /**
+ * Device capability
+ */
+ public int deviceCapability;
+
+ /**
+ * Group capability
+ */
+ public int groupCapability;
+
+ public enum Status {
+ CONNECTED,
+ INVITED,
+ FAILED,
+ AVAILABLE,
+ UNAVAILABLE,
+ }
+
+ public Status status = Status.UNAVAILABLE;
+
+ public WifiP2pDevice() {
+ }
+
+ /**
+ * @param string formats supported include
+ * P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * P2P-DEVICE-LOST p2p_dev_addr=fa:7b:7a:42:02:13
+ *
+ * fa:7b:7a:42:02:13
+ *
+ * P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
+ * pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
+ * group_capab=0x0
+ *
+ * Note: The events formats can be looked up in the wpa_supplicant code
+ */
+ public WifiP2pDevice(String string) throws IllegalArgumentException {
+ String[] tokens = string.split(" ");
+
+ if (tokens.length < 1) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+
+ /* Just a device address */
+ if (tokens.length == 1) {
+ deviceAddress = string;
+ return;
+ }
+
+ Pattern p = Pattern.compile("(?:[0-9a-f]{2}:){5}[0-9a-f]{2}", Pattern.CASE_INSENSITIVE);
+ if (p.matcher(tokens[1]).matches()) interfaceAddress = tokens[1];
+
+ for (String token : tokens) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) continue;
+
+ if (nameValue[0].equals("p2p_dev_addr")) {
+ deviceAddress = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("pri_dev_type")) {
+ primaryDeviceType = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("name")) {
+ deviceName = trimQuotes(nameValue[1]);
+ }
+
+ if (nameValue[0].equals("config_methods")) {
+ wpsConfigMethodsSupported = parseHex(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("dev_capab")) {
+ deviceCapability = parseHex(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("group_capab")) {
+ groupCapability = parseHex(nameValue[1]);
+ continue;
+ }
+ }
+
+ if (tokens[0].startsWith("P2P-DEVICE-FOUND")) {
+ status = Status.AVAILABLE;
+ }
+ }
+
+ public boolean isGroupOwner() {
+ return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof WifiP2pDevice)) return false;
+
+ WifiP2pDevice other = (WifiP2pDevice) obj;
+ if (other == null || other.deviceAddress == null) {
+ return (deviceAddress == null);
+ }
+ //STOPSHIP: fix later
+ //return other.deviceAddress.equals(deviceAddress);
+ return other.deviceAddress.startsWith(deviceAddress.substring(0,8));
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("Device: ").append(deviceName);
+ sbuf.append("\n deviceAddress: ").append(deviceAddress);
+ sbuf.append("\n interfaceAddress: ").append(interfaceAddress);
+ sbuf.append("\n primary type: ").append(primaryDeviceType);
+ sbuf.append("\n secondary type: ").append(secondaryDeviceType);
+ sbuf.append("\n wps: ").append(wpsConfigMethodsSupported);
+ sbuf.append("\n grpcapab: ").append(groupCapability);
+ sbuf.append("\n devcapab: ").append(deviceCapability);
+ sbuf.append("\n status: ").append(status);
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ public WifiP2pDevice(WifiP2pDevice source) {
+ if (source != null) {
+ deviceName = source.deviceName;
+ deviceAddress = source.deviceAddress;
+ interfaceAddress = source.interfaceAddress;
+ primaryDeviceType = source.primaryDeviceType;
+ secondaryDeviceType = source.secondaryDeviceType;
+ wpsConfigMethodsSupported = source.wpsConfigMethodsSupported;
+ deviceCapability = source.deviceCapability;
+ groupCapability = source.groupCapability;
+ status = source.status;
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(deviceName);
+ dest.writeString(deviceAddress);
+ dest.writeString(interfaceAddress);
+ dest.writeString(primaryDeviceType);
+ dest.writeString(secondaryDeviceType);
+ dest.writeInt(wpsConfigMethodsSupported);
+ dest.writeInt(deviceCapability);
+ dest.writeInt(groupCapability);
+ dest.writeString(status.name());
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pDevice> CREATOR =
+ new Creator<WifiP2pDevice>() {
+ public WifiP2pDevice createFromParcel(Parcel in) {
+ WifiP2pDevice device = new WifiP2pDevice();
+ device.deviceName = in.readString();
+ device.deviceAddress = in.readString();
+ device.interfaceAddress = in.readString();
+ device.primaryDeviceType = in.readString();
+ device.secondaryDeviceType = in.readString();
+ device.wpsConfigMethodsSupported = in.readInt();
+ device.deviceCapability = in.readInt();
+ device.groupCapability = in.readInt();
+ device.status = Status.valueOf(in.readString());
+ return device;
+ }
+
+ public WifiP2pDevice[] newArray(int size) {
+ return new WifiP2pDevice[size];
+ }
+ };
+
+ private String trimQuotes(String str) {
+ str = str.trim();
+ if (str.startsWith("'") && str.endsWith("'")) {
+ return str.substring(1, str.length()-1);
+ }
+ return str;
+ }
+
+ //supported formats: 0x1abc, 0X1abc, 1abc
+ private int parseHex(String hexString) {
+ int num = 0;
+ if (hexString.startsWith("0x") || hexString.startsWith("0X")) {
+ hexString = hexString.substring(2);
+ }
+
+ try {
+ num = Integer.parseInt(hexString, 16);
+ } catch(NumberFormatException e) {
+ Log.e(TAG, "Failed to parse hex string " + hexString);
+ }
+ return num;
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl
new file mode 100644
index 0000000..6c79009
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pDeviceList;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
new file mode 100644
index 0000000..4ec23b8
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A class representing a Wi-Fi P2p device list
+ * @hide
+ */
+public class WifiP2pDeviceList implements Parcelable {
+
+ private Collection<WifiP2pDevice> mDevices;
+
+ public WifiP2pDeviceList() {
+ mDevices = new ArrayList<WifiP2pDevice>();
+ }
+
+ //copy constructor
+ public WifiP2pDeviceList(WifiP2pDeviceList source) {
+ if (source != null) {
+ mDevices = source.getDeviceList();
+ }
+ }
+
+ public WifiP2pDeviceList(ArrayList<WifiP2pDevice> devices) {
+ mDevices = new ArrayList<WifiP2pDevice>();
+ for (WifiP2pDevice device : devices) {
+ mDevices.add(device);
+ }
+ }
+
+ public void clear() {
+ mDevices.clear();
+ }
+
+ public void add(WifiP2pDevice device) {
+ if (device == null) return;
+ if (mDevices.contains(device)) return;
+ mDevices.add(device);
+ }
+
+ public boolean remove(WifiP2pDevice device) {
+ if (device == null) return false;
+ return mDevices.remove(device);
+ }
+
+ public Collection<WifiP2pDevice> getDeviceList() {
+ return Collections.unmodifiableCollection(mDevices);
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ for (WifiP2pDevice device : mDevices) {
+ sbuf.append("\n").append(device);
+ }
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDevices.size());
+ for(WifiP2pDevice device : mDevices) {
+ dest.writeParcelable(device, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pDeviceList> CREATOR =
+ new Creator<WifiP2pDeviceList>() {
+ public WifiP2pDeviceList createFromParcel(Parcel in) {
+ WifiP2pDeviceList deviceList = new WifiP2pDeviceList();
+
+ int deviceCount = in.readInt();
+ for (int i = 0; i < deviceCount; i++) {
+ deviceList.add((WifiP2pDevice)in.readParcelable(null));
+ }
+ return deviceList;
+ }
+
+ public WifiP2pDeviceList[] newArray(int size) {
+ return new WifiP2pDeviceList[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl
new file mode 100644
index 0000000..403f2b1
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pGroup;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
new file mode 100644
index 0000000..ca6e4d5
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A class representing a Wi-Fi P2p group
+ * @hide
+ */
+public class WifiP2pGroup implements Parcelable {
+
+ /** The network name */
+ private String mNetworkName;
+
+ /** The network bssid */
+ private String mNetworkBssid;
+
+ /** Group owner */
+ private WifiP2pDevice mOwner;
+
+ /** Device is group owner */
+ private boolean mIsGroupOwner;
+
+ /** Group clients */
+ private List<WifiP2pDevice> mClients = new ArrayList<WifiP2pDevice>();
+
+ private int mChannel;
+
+ /**
+ * The network passphrase
+ * <p/>
+ * The passphrase used for WPA2-PSK
+ */
+ private String mPassphrase;
+
+ /**
+ * TODO: fix
+ * Sometimes supplicant sends a psk
+ */
+ private String mPsk;
+
+ /** Indicates that the group is persistent */
+ private boolean mIsPersistent;
+
+ private String mInterface;
+
+ public WifiP2pGroup() {
+ }
+
+ /**
+ * @param string formats supported include
+ *
+ * P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
+ * [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|
+ * passphrase="fKG4jMe3"] go_dev_addr=fa:7b:7a:42:02:13
+ *
+ * P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED
+ *
+ * P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
+ * bssid=fa:7b:7a:42:82:13 unknown-network
+ *
+ * Note: The events formats can be looked up in the wpa_supplicant code
+ */
+ public WifiP2pGroup(String supplicantEvent) throws IllegalArgumentException {
+
+ String[] tokens = supplicantEvent.split(" ");
+
+ if (tokens.length < 3) {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+
+ if (tokens[0].startsWith("P2P-GROUP")) {
+ mInterface = tokens[1];
+ mIsGroupOwner = tokens[2].equals("GO");
+
+ for (String token : tokens) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) continue;
+
+ if (nameValue[0].equals("ssid")) {
+ mNetworkName = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("freq")) {
+ try {
+ mChannel = Integer.parseInt(nameValue[1]);
+ } catch (NumberFormatException e) {
+ mChannel = 0; //invalid
+ }
+ continue;
+ }
+
+ if (nameValue[0].equals("psk")) {
+ mPsk = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("passphrase")) {
+ mPassphrase = nameValue[1];
+ continue;
+ }
+
+ if (nameValue[0].equals("go_dev_addr")) {
+ mOwner = new WifiP2pDevice(nameValue[1]);
+ }
+ }
+ } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
+ for (String token : tokens) {
+ String[] nameValue = token.split("=");
+ if (nameValue.length != 2) continue;
+
+ if (nameValue[0].equals("go_dev_addr")) {
+ mOwner = new WifiP2pDevice(nameValue[1]);
+ continue;
+ }
+
+ if (nameValue[0].equals("bssid")) {
+ mNetworkBssid = nameValue[1];
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Malformed supplicant event");
+ }
+ }
+
+ public boolean isGroupOwner() {
+ return mIsGroupOwner;
+ }
+
+ public WifiP2pDevice getOwner() {
+ return mOwner;
+ }
+
+ public void addClient(String address) {
+ addClient(new WifiP2pDevice(address));
+ }
+
+ public void addClient(WifiP2pDevice device) {
+ for (WifiP2pDevice client : mClients) {
+ if (client.equals(device)) return;
+ }
+ mClients.add(device);
+ }
+
+ public boolean removeClient(String address) {
+ return mClients.remove(new WifiP2pDevice(address));
+ }
+
+ public boolean removeClient(WifiP2pDevice device) {
+ return mClients.remove(device);
+ }
+
+ public boolean isClientListEmpty() {
+ return mClients.size() == 0;
+ }
+
+ public Collection<WifiP2pDevice> getClientList() {
+ return Collections.unmodifiableCollection(mClients);
+ }
+
+ public String getInterface() {
+ return mInterface;
+ }
+
+ // TODO: implement
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ //sbuf.append("SSID: ").append(SSID);
+ //sbuf.append("\n passphrase: ").append(passphrase);
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ // TODO: implement
+ public WifiP2pGroup(WifiP2pGroup source) {
+ if (source != null) {
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ // STOPSHIP: implement
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pGroup> CREATOR =
+ new Creator<WifiP2pGroup>() {
+ public WifiP2pGroup createFromParcel(Parcel in) {
+ WifiP2pGroup group = new WifiP2pGroup();
+ return group;
+ }
+
+ public WifiP2pGroup[] newArray(int size) {
+ return new WifiP2pGroup[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
new file mode 100644
index 0000000..ea212ac
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.WorkSource;
+import android.os.Messenger;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+/**
+ * This class provides the API for managing Wi-Fi p2p
+ * connectivity. Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService(Context.WIFI_P2P_SERVICE)}.
+ *
+ * It deals with the following:
+ * <ul>
+ * <li>Wi-Fi peer discovery and connection setup. Allows applications to initiate a discovery to
+ * find available peers and then setup a connection </li>
+ * <li>Configuration and status query. Allows applications to fetch the current list
+ * of available and connected peers and query connection status </li>
+ * <li>Intent actions that are broadcast to track operations
+ * on a p2p connection</li>
+ * </ul>
+ * @hide
+ */
+public class WifiP2pManager {
+ /**
+ * Broadcast intent action to indicate whether Wi-Fi p2p is enabled or disabled.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_P2P_STATE_CHANGED_ACTION =
+ "android.net.wifi.P2P_STATE_CHANGED";
+
+ /**
+ * The lookup key for an int that indicates whether Wi-Fi p2p is enabled or disabled.
+ * Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}.
+ *
+ * @see #WIFI_P2P_STATE_DISABLED
+ * @see #WIFI_P2P_STATE_ENABLED
+ */
+ public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
+
+ /**
+ * Wi-Fi p2p is disabled.
+ *
+ * @see #WIFI_P2P_STATE_CHANGED_ACTION
+ * @see #getWifiP2pState()
+ */
+ public static final int WIFI_P2P_STATE_DISABLED = 1;
+
+ /**
+ * Wi-Fi p2p is enabled.
+ *
+ * @see #WIFI_P2P_STATE_CHANGED_ACTION
+ * @see #getWifiP2pState()
+ */
+ public static final int WIFI_P2P_STATE_ENABLED = 2;
+
+ /**
+ * Broadcast intent action indicating that the state of Wi-Fi p2p connectivity
+ * has changed. One extra provides the new state
+ * in the form of a {@link android.net.NetworkInfo} object.
+ * @see #EXTRA_NETWORK_INFO
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION =
+ "android.net.wifi.CONNECTION_STATE_CHANGE";
+
+ /**
+ * The lookup key for a {@link android.net.NetworkInfo} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_NETWORK_INFO = "networkInfo";
+
+ /**
+ * Broadcast intent action indicating that the available peer list has changed
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String WIFI_P2P_PEERS_CHANGED_ACTION =
+ "android.net.wifi.PEERS_CHANGED";
+
+ /**
+ * Activity Action: Pick a Wi-Fi p2p network to connect to.
+ * <p>Input: Nothing.
+ * <p>Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PICK_WIFI_P2P_NETWORK =
+ "android.net.wifi.PICK_WIFI_P2P_NETWORK";
+
+ IWifiP2pManager mService;
+
+ /* For communication with WifiP2pService */
+ private AsyncChannel mAsyncChannel = new AsyncChannel();
+
+ /* AsyncChannel notifications to apps */
+ public static final int HANDLER_CONNECTION = AsyncChannel.CMD_CHANNEL_HALF_CONNECTED;
+ public static final int HANDLER_DISCONNECTION = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
+
+ private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
+
+ public static final int ENABLE_P2P = BASE + 1;
+ public static final int ENABLE_P2P_FAILED = BASE + 2;
+ public static final int ENABLE_P2P_SUCCEEDED = BASE + 3;
+
+ /* arg1 on ENABLE_P2P_FAILED indicates a reason for failure */
+ public static final int P2P_UNSUPPORTED = 1;
+
+ public static final int DISABLE_P2P = BASE + 5;
+ public static final int DISABLE_P2P_FAILED = BASE + 6;
+ public static final int DISABLE_P2P_SUCCEEDED = BASE + 7;
+
+ public static final int START_LISTEN_MODE = BASE + 9;
+ public static final int START_LISTEN_FAILED = BASE + 10;
+ public static final int START_LISTEN_SUCCEEDED = BASE + 11;
+
+ public static final int DISCOVER_PEERS = BASE + 13;
+ public static final int DISCOVER_PEERS_FAILED = BASE + 14;
+ public static final int DISCOVER_PEERS_SUCCEDED = BASE + 15;
+
+ public static final int CANCEL_DISCOVER_PEERS = BASE + 17;
+ public static final int CANCEL_DISCOVER_PEERS_FAILED = BASE + 18;
+ public static final int CANCEL_DISCOVER_PEERS_SUCCEDED = BASE + 19;
+
+ public static final int CONNECT = BASE + 21;
+ public static final int CONNECT_FAILED = BASE + 22;
+ public static final int CONNECT_SUCCEEDED = BASE + 23;
+
+ public static final int CANCEL_CONNECT = BASE + 25;
+ public static final int CANCEL_CONNECT_FAILED = BASE + 26;
+ public static final int CANCEL_CONNECT_SUCCEDED = BASE + 27;
+
+ public static final int REJECT = BASE + 28;
+ public static final int REJECT_FAILED = BASE + 29;
+ public static final int REJECT_SUCCEEDED = BASE + 30;
+
+ public static final int CREATE_GROUP = BASE + 31;
+ public static final int CREATE_GROUP_FAILED = BASE + 32;
+ public static final int CREATE_GROUP_SUCCEEDED = BASE + 33;
+
+ public static final int REMOVE_GROUP = BASE + 34;
+ public static final int REMOVE_GROUP_FAILED = BASE + 35;
+ public static final int REMOVE_GROUP_SUCCEEDED = BASE + 36;
+
+ public static final int REQUEST_SETTINGS = BASE + 37;
+ public static final int RESPONSE_SETTINGS = BASE + 38;
+
+ public static final int REQUEST_PEERS = BASE + 39;
+ public static final int RESPONSE_PEERS = BASE + 40;
+
+ public static final int REQUEST_CONNECTION_STATUS = BASE + 41;
+ public static final int RESPONSE_CONNECTION_STATUS = BASE + 42;
+
+ public static final int WPS_PBC = BASE + 43;
+ public static final int WPS_PIN = BASE + 44;
+ public static final int WPS_PIN_AVAILABLE = BASE + 45;
+
+ /**
+ * Create a new WifiP2pManager instance. Applications use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * the standard {@link android.content.Context#WIFI_P2P_SERVICE Context.WIFI_P2P_SERVICE}.
+ * @param service the Binder interface
+ * @param handler target for messages
+ * @hide - hide this because it takes in a parameter of type IWifiP2pManager, which
+ * is a system private class.
+ */
+ public WifiP2pManager(IWifiP2pManager service) {
+ mService = service;
+ }
+
+ /**
+ * Registers the application handler with the Wi-Fi framework.
+ * This function must be the first to be called before any p2p control
+ * or query operations can be performed.
+ * @param srcContext is the context of the source
+ * @param srcHandler is the handler on which the source receives messages
+ * @return {@code true} if the operation succeeded
+ */
+ public boolean connectHandler(Context srcContext, Handler srcHandler) {
+ Messenger messenger = getMessenger();
+ if (messenger == null) return false;
+ return mAsyncChannel.connectSync(srcContext, srcHandler, messenger)
+ == AsyncChannel.STATUS_SUCCESSFUL;
+ }
+
+ public boolean isP2pSupported() {
+ try {
+ return mService.isP2pSupported();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sends in a request to the system to enable p2p. This will pop up a dialog
+ * to the user and upon authorization will enable p2p.
+ */
+ public void enableP2p() {
+ mAsyncChannel.sendMessage(ENABLE_P2P);
+ }
+
+ /**
+ * Sends in a request to the system to disable p2p. This will pop up a dialog
+ * to the user and upon authorization will enable p2p.
+ */
+ public void disableP2p() {
+ mAsyncChannel.sendMessage(DISABLE_P2P);
+ }
+
+ /**
+ * Set device in listen mode. This will make the device discoverable by
+ * another peer.
+ * A dialog to the user is thrown to request his permission since it can
+ * have a significant impact on power consumption
+ */
+ public void setListenState(int timeout) {
+ mAsyncChannel.sendMessage(START_LISTEN_MODE, timeout);
+ }
+
+ /**
+ * Initiates peer discovery
+ */
+ public void discoverPeers() {
+ mAsyncChannel.sendMessage(DISCOVER_PEERS);
+ }
+
+ /**
+ * Initiates peer discovery with a timeout
+ */
+ public void discoverPeers(int timeout) {
+ mAsyncChannel.sendMessage(DISCOVER_PEERS, timeout);
+ }
+
+ /**
+ * Cancel any existing peer discovery operation
+ */
+ public void cancelPeerDiscovery() {
+ mAsyncChannel.sendMessage(CANCEL_DISCOVER_PEERS);
+ }
+
+ /**
+ * Start a p2p connection
+ *
+ * @param peer Configuration described in a {@link WifiP2pConfig} object.
+ */
+ public void connect(WifiP2pConfig config) {
+ mAsyncChannel.sendMessage(CONNECT, config);
+ }
+
+ /**
+ * Cancel any ongoing negotiation or disconnect from an existing group
+ */
+ public void disconnect() {
+ mAsyncChannel.sendMessage(CANCEL_CONNECT);
+ }
+
+ /**
+ * Create a p2p group. This is essentially an access point that can accept
+ * client connections.
+ */
+ public void createGroup() {
+ mAsyncChannel.sendMessage(CREATE_GROUP);
+ }
+
+ /**
+ * Remove the current group. This also removes the p2p interface created
+ * during group formation.
+ */
+ public void removeGroup() {
+ mAsyncChannel.sendMessage(REMOVE_GROUP);
+ }
+
+ /**
+ * Request current p2p settings. This returns a RESPONSE_SETTINGS on the source
+ * handler.
+ */
+ public void requestP2pSettings() {
+ mAsyncChannel.sendMessage(REQUEST_SETTINGS);
+ }
+
+ /**
+ * Request the list of peers. This returns a RESPONSE_PEERS on the source
+ * handler.
+ */
+ public void requestPeers() {
+ mAsyncChannel.sendMessage(REQUEST_PEERS);
+ }
+
+ /**
+ * Fetch device list from a RESPONSE_PEERS message
+ */
+ public WifiP2pDeviceList peersInResponse(Message msg) {
+ return (WifiP2pDeviceList) msg.obj;
+ }
+
+ /**
+ * Request device connection status. This returns a RESPONSE_CONNECTION_STATUS on
+ * the source handler.
+ */
+ public void requestConnectionStatus() {
+ mAsyncChannel.sendMessage(REQUEST_CONNECTION_STATUS);
+ }
+
+
+ /**
+ * Get a reference to WifiP2pService handler. This is used to establish
+ * an AsyncChannel communication with WifiService
+ *
+ * @return Messenger pointing to the WifiP2pService handler
+ * @hide
+ */
+ public Messenger getMessenger() {
+ try {
+ return mService.getMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
new file mode 100644
index 0000000..4988f0b
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2011 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.server;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiMonitor;
+import android.net.wifi.WifiNative;
+import android.net.wifi.WifiStateMachine;
+import android.net.wifi.WpsConfiguration;
+import android.net.wifi.WpsConfiguration.Setup;
+import android.net.wifi.p2p.IWifiP2pManager;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pDevice.Status;
+import android.net.wifi.p2p.WifiP2pDeviceList;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Messenger;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.R;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+/**
+ * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
+ * communicate with this service to issue device discovery and connectivity requests
+ * through the WifiP2pManager interface. The state machine communicates with the wifi
+ * driver through wpa_supplicant and handles the event responses through WifiMonitor.
+ *
+ * Note that the term Wifi when used without a p2p suffix refers to the client mode
+ * of Wifi operation
+ * @hide
+ */
+public class WifiP2pService extends IWifiP2pManager.Stub {
+ private static final String TAG = "WifiP2pService";
+ private static final boolean DBG = true;
+
+ private Context mContext;
+
+ // Tracked to notify the user about wifi client/hotspot being shut down
+ // during p2p bring up
+ private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
+ private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
+
+ private P2pStateMachine mP2pStateMachine;
+ private AsyncChannel mReplyChannel = new AsyncChannel();;
+ private AsyncChannel mWifiChannel;
+
+ private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
+
+ /* Message sent to WifiStateMachine to indicate p2p enable is pending */
+ public static final int P2P_ENABLE_PENDING = BASE + 1;
+ /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
+ public static final int WIFI_ENABLE_PROCEED = BASE + 2;
+
+ /* User accepted to disable Wi-Fi in order to enable p2p */
+ private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 11;
+
+ private final boolean mP2pSupported;
+
+ public WifiP2pService(Context context) {
+ mContext = context;
+
+ mP2pSupported = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_p2p_support);
+
+ mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
+ mP2pStateMachine.start();
+
+ // broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(new WifiStateReceiver(), filter);
+
+ }
+
+ private class WifiStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_DISABLED);
+ } else if (intent.getAction().equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+ mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
+ WifiManager.WIFI_AP_STATE_DISABLED);
+ }
+ }
+ }
+
+ private void enforceAccessPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
+ "WifiP2pService");
+ }
+
+ private void enforceChangePermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
+ "WifiP2pService");
+ }
+
+ /**
+ * Get a reference to handler. This is used by a client to establish
+ * an AsyncChannel communication with WifiP2pService
+ */
+ public Messenger getMessenger() {
+ enforceAccessPermission();
+ enforceChangePermission();
+ return new Messenger(mP2pStateMachine.getHandler());
+ }
+
+ /**
+ * Return if p2p is supported
+ */
+ public boolean isP2pSupported() {
+ enforceAccessPermission();
+ return mP2pSupported;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump WifiP2pService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ }
+
+
+ /**
+ * Handles interaction with WifiStateMachine
+ */
+ private class P2pStateMachine extends StateMachine {
+
+ private DefaultState mDefaultState = new DefaultState();
+ private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
+ private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
+ private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
+ private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
+ private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
+ private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
+ // Inactive is when p2p is enabled with no connectivity
+ private InactiveState mInactiveState = new InactiveState();
+ private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
+ private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
+
+ private WifiMonitor mWifiMonitor = new WifiMonitor(this);
+
+ private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+ private WifiP2pGroup mGroup;
+
+ // Saved enable request message so the state machine can send an appropriate response
+ private Message mSavedEnableRequestMessage;
+
+ // Saved WifiP2pConfig from GO negotiation request
+ private WifiP2pConfig mSavedGoNegotiationConfig;
+
+ // Saved WifiP2pConfig from connect request
+ private WifiP2pConfig mSavedConnectConfig;
+
+ // Saved WifiP2pGroup from invitation request
+ private WifiP2pGroup mSavedP2pGroup;
+
+ P2pStateMachine(String name, boolean p2pSupported) {
+ super(name);
+
+ addState(mDefaultState);
+ addState(mP2pNotSupportedState, mDefaultState);
+ addState(mP2pDisablingState, mDefaultState);
+ addState(mP2pDisabledState, mDefaultState);
+ addState(mWaitForWifiDisableState, mDefaultState);
+ addState(mP2pEnablingState, mDefaultState);
+ addState(mP2pEnabledState, mDefaultState);
+ addState(mInactiveState, mP2pEnabledState);
+ addState(mGroupNegotiationState, mP2pEnabledState);
+ addState(mGroupCreatedState, mP2pEnabledState);
+
+ if (p2pSupported) {
+ setInitialState(mP2pDisabledState);
+ } else {
+ setInitialState(mP2pNotSupportedState);
+ }
+ }
+
+ // TODO: Respond to every p2p request with success/failure
+ class DefaultState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ if (DBG) Slog.d(TAG, "Full connection with WifiStateMachine established");
+ mWifiChannel = (AsyncChannel) message.obj;
+ } else {
+ Slog.e(TAG, "Full connection failure, error = " + message.arg1);
+ mWifiChannel = null;
+ }
+ break;
+
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ Slog.e(TAG, "Send failed, client connection lost");
+ } else {
+ Slog.e(TAG, "Client connection lost with reason: " + message.arg1);
+ }
+ mWifiChannel = null;
+ break;
+
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ AsyncChannel ac = new AsyncChannel();
+ ac.connect(mContext, getHandler(), message.replyTo);
+ break;
+ case WifiStateMachine.WIFI_ENABLE_PENDING:
+ // Disable p2p operation before we can respond
+ sendMessage(WifiP2pManager.DISABLE_P2P);
+ deferMessage(message);
+ break;
+ case WifiP2pManager.ENABLE_P2P:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED);
+ break;
+ case WifiP2pManager.DISABLE_P2P:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED);
+ break;
+ case WifiP2pManager.START_LISTEN_MODE:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED);
+ break;
+ case WifiP2pManager.CANCEL_DISCOVER_PEERS:
+ mReplyChannel.replyToMessage(message,
+ WifiP2pManager.CANCEL_DISCOVER_PEERS_FAILED);
+ break;
+ case WifiP2pManager.CONNECT:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+ break;
+ case WifiP2pManager.CANCEL_CONNECT:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED);
+ break;
+ case WifiP2pManager.REJECT:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.REJECT_FAILED);
+ break;
+ case WifiP2pManager.CREATE_GROUP:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED);
+ break;
+ case WifiP2pManager.REMOVE_GROUP:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED);
+ break;
+ // TODO: fix
+ case WifiP2pManager.REQUEST_SETTINGS:
+ case WifiP2pManager.REQUEST_PEERS:
+ case WifiP2pManager.REQUEST_CONNECTION_STATUS:
+ break;
+ // Ignore
+ case WIFI_DISABLE_USER_ACCEPT:
+ break;
+ default:
+ Slog.e(TAG, "Unhandled message " + message);
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pNotSupportedState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ // Allow Wi-Fi to proceed
+ case WifiStateMachine.WIFI_ENABLE_PENDING:
+ mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED);
+ break;
+ case WifiP2pManager.ENABLE_P2P:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.DISABLE_P2P:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pDisablingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ // TODO: fix later
+ WifiNative.unloadDriver();
+ transitionTo(mP2pDisabledState);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.SUP_DISCONNECTION_EVENT:
+ WifiNative.unloadDriver();
+ transitionTo(mP2pDisabledState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+
+ class P2pDisabledState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pManager.ENABLE_P2P:
+ mSavedEnableRequestMessage = Message.obtain(message);
+ OnClickListener listener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ sendMessage(WIFI_DISABLE_USER_ACCEPT);
+ } else {
+ mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
+ WifiP2pManager.ENABLE_P2P_FAILED);
+ }
+ }
+ };
+
+ // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
+ if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
+ mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
+ Resources r = Resources.getSystem();
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
+ .setPositiveButton(r.getString(R.string.ok), listener)
+ .setNegativeButton(r.getString(R.string.cancel), listener)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ } else {
+ mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
+ transitionTo(mWaitForWifiDisableState);
+ }
+ break;
+ case WIFI_DISABLE_USER_ACCEPT:
+ mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
+ transitionTo(mWaitForWifiDisableState);
+ break;
+ case WifiStateMachine.WIFI_ENABLE_PENDING:
+ mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class WaitForWifiDisableState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiStateMachine.P2P_ENABLE_PROCEED:
+ // TODO: fix this for p2p
+ if (WifiNative.loadDriver() &&
+ WifiNative.startSupplicant()) {
+ Slog.d(TAG, "Wi-fi Direct start successful");
+ mWifiMonitor.startMonitoring();
+ transitionTo(mP2pEnablingState);
+ } else {
+ notifyP2pEnableFailure();
+ mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
+ WifiP2pManager.ENABLE_P2P_FAILED);
+ transitionTo(mP2pDisabledState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pEnablingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.SUP_CONNECTION_EVENT:
+ mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
+ WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+ transitionTo(mInactiveState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pEnabledState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ sendP2pStateChangedBroadcast(true);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pManager.DISABLE_P2P:
+ // TODO: use stopSupplicant after control channel fixed
+ WifiNative.killSupplicant();
+ transitionTo(mP2pDisablingState);
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
+ int timeout = message.arg1;
+ WifiNative.p2pFlush();
+ WifiNative.p2pFind(timeout);
+ break;
+ case WifiP2pManager.REQUEST_PEERS:
+ mReplyChannel.replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
+ break;
+ case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
+ WifiP2pDevice device = (WifiP2pDevice) message.obj;
+ mPeers.add(device);
+ sendP2pPeersChangedBroadcast();
+ break;
+ case WifiMonitor.P2P_DEVICE_LOST_EVENT:
+ device = (WifiP2pDevice) message.obj;
+ if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
+ break;
+ case WifiP2pManager.CONNECT:
+ if (DBG) Slog.d(TAG, getName() + " sending connect");
+ mSavedConnectConfig = (WifiP2pConfig) message.obj;
+ String pin = WifiNative.p2pConnect(mSavedConnectConfig);
+ try {
+ Integer.parseInt(pin);
+ notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
+ } catch (NumberFormatException ignore) {
+ // do nothing if p2pConnect did not return a pin
+ }
+ updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.INVITED);
+ sendP2pPeersChangedBroadcast();
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiP2pManager.REJECT:
+ if (DBG) Slog.d(TAG, getName() + " sending reject");
+ WifiNative.p2pReject((String) message.obj);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ sendP2pStateChangedBroadcast(false);
+ }
+ }
+
+ class InactiveState extends State {
+ @Override public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
+ mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
+ notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
+ break;
+ case WifiP2pManager.CREATE_GROUP:
+ WifiNative.p2pGroupAdd();
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
+ WifiP2pGroup group = (WifiP2pGroup) message.obj;
+ notifyP2pInvitationReceived(group);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class GroupNegotiationState extends State {
+ @Override public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ // We ignore these right now, since we get a GROUP_STARTED notification
+ // afterwards
+ case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+ case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
+ if (DBG) Slog.d(TAG, getName() + " go success");
+ break;
+ case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+ case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ if (DBG) Slog.d(TAG, getName() + " go failure");
+ updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.FAILED);
+ mSavedConnectConfig = null;
+ transitionTo(mInactiveState);
+ break;
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) Slog.d(TAG, getName() + " group started");
+ // If this device is GO, do nothing since there is a follow up
+ // AP_STA_CONNECTED event
+ if (!mGroup.isGroupOwner()) {
+ WifiP2pDevice groupOwner = mGroup.getOwner();
+ updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED);
+ sendP2pPeersChangedBroadcast();
+ }
+ transitionTo(mGroupCreatedState);
+ break;
+ case WifiP2pManager.CANCEL_CONNECT:
+ // TODO: fix
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class GroupCreatedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) Slog.d(TAG, getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Slog.d(TAG, getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.AP_STA_CONNECTED_EVENT:
+ String address = (String) message.obj;
+ mGroup.addClient(address);
+ updateDeviceStatus(address, Status.CONNECTED);
+ if (DBG) Slog.d(TAG, getName() + " ap sta connected");
+ sendP2pPeersChangedBroadcast();
+ break;
+ case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
+ address = (String) message.obj;
+ updateDeviceStatus(address, Status.AVAILABLE);
+ if (mGroup.removeClient(address)) {
+ if (DBG) Slog.d(TAG, "Removed client " + address);
+ if (mGroup.isClientListEmpty()) {
+ Slog.d(TAG, "Client list empty, killing p2p connection");
+ sendMessage(WifiP2pManager.REMOVE_GROUP);
+ } else {
+ // Just send a notification
+ sendP2pPeersChangedBroadcast();
+ }
+ } else {
+ if (DBG) Slog.d(TAG, "Failed to remove client " + address);
+ for (WifiP2pDevice c : mGroup.getClientList()) {
+ if (DBG) Slog.d(TAG,"client " + c.deviceAddress);
+ }
+ }
+ if (DBG) Slog.e(TAG, getName() + " ap sta disconnected");
+ break;
+ // Disconnect & remove group have same effect when connected
+ case WifiP2pManager.CANCEL_CONNECT:
+ case WifiP2pManager.REMOVE_GROUP:
+ if (DBG) Slog.e(TAG, getName() + " remove group");
+ WifiNative.p2pFlush();
+ WifiNative.p2pGroupRemove(mGroup.getInterface());
+ break;
+ case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
+ if (DBG) Slog.e(TAG, getName() + " group removed");
+ Collection <WifiP2pDevice> devices = mGroup.getClientList();
+ boolean changed = false;
+ for (WifiP2pDevice d : mPeers.getDeviceList()) {
+ if (devices.contains(d) || mGroup.getOwner().equals(d)) {
+ d.status = Status.AVAILABLE;
+ changed = true;
+ }
+ }
+ mGroup = null;
+ if (changed) sendP2pPeersChangedBroadcast();
+ transitionTo(mInactiveState);
+ break;
+ case WifiMonitor.P2P_DEVICE_LOST_EVENT:
+ WifiP2pDevice device = (WifiP2pDevice) message.obj;
+ if (device.equals(mGroup.getOwner())) {
+ Slog.d(TAG, "Lost the group owner, killing p2p connection");
+ sendMessage(WifiP2pManager.REMOVE_GROUP);
+ } else if (mGroup.removeClient(device) && mGroup.isClientListEmpty()) {
+ Slog.d(TAG, "Client list empty, killing p2p connection");
+ sendMessage(WifiP2pManager.REMOVE_GROUP);
+ }
+ return NOT_HANDLED; // Do the regular device lost handling
+ case WifiP2pManager.DISABLE_P2P:
+ sendMessage(WifiP2pManager.REMOVE_GROUP);
+ deferMessage(message);
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
+ int timeout = message.arg1;
+ WifiNative.p2pFind(timeout);
+ break;
+ case WifiP2pManager.CONNECT:
+ WifiP2pConfig config = (WifiP2pConfig) message.obj;
+ Slog.d(TAG, "Inviting device : " + config.deviceAddress);
+ WifiNative.p2pInvite(mGroup, config.deviceAddress);
+ updateDeviceStatus(config.deviceAddress, Status.INVITED);
+ sendP2pPeersChangedBroadcast();
+ // TODO: figure out updating the status to declined when invitation is rejected
+ break;
+ case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
+ Slog.d(TAG,"===> INVITATION RESULT EVENT : " + message.obj);
+ break;
+ case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj);
+ break;
+ case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj);
+ break;
+ case WifiP2pManager.WPS_PBC:
+ WifiNative.p2pWpsPbc();
+ break;
+ case WifiP2pManager.WPS_PIN:
+ WifiNative.p2pWpsPin((String) message.obj);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private void sendP2pStateChangedBroadcast(boolean enabled) {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (enabled) {
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_ENABLED);
+ } else {
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_DISABLED);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendP2pPeersChangedBroadcast() {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void notifyP2pEnableFailure() {
+ Resources r = Resources.getSystem();
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setMessage(r.getString(R.string.wifi_p2p_failed_message))
+ .setPositiveButton(r.getString(R.string.ok), null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void notifyWpsPin(String pin, String peerAddress) {
+ Resources r = Resources.getSystem();
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setMessage(r.getString(R.string.wifi_p2p_pin_display_message, pin, peerAddress))
+ .setPositiveButton(r.getString(R.string.ok), null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void notifyP2pGoNegotationRequest(WifiP2pConfig config) {
+ Resources r = Resources.getSystem();
+ WpsConfiguration wpsConfig = config.wpsConfig;
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
+ final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (DBG) Slog.d(TAG, getName() + " connect " + pin.getText());
+ mSavedGoNegotiationConfig.wpsConfig.setup = Setup.KEYPAD;
+ mSavedGoNegotiationConfig.wpsConfig.pin = pin.getText().toString();
+ sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
+ mSavedGoNegotiationConfig = null;
+ }
+ })
+ .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DBG) Slog.d(TAG, getName() + " reject");
+ sendMessage(WifiP2pManager.REJECT,
+ mSavedGoNegotiationConfig.deviceAddress);
+ mSavedGoNegotiationConfig = null;
+ }
+ })
+ .create();
+
+ if (wpsConfig.setup == Setup.PBC) {
+ pin.setVisibility(View.GONE);
+ dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
+ config.deviceAddress));
+ } else {
+ dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
+ config.deviceAddress));
+ }
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void notifyP2pProvDiscPbcRequest(WifiP2pDevice peer) {
+ Resources r = Resources.getSystem();
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
+ final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (DBG) Slog.d(TAG, getName() + " wps_pbc");
+ sendMessage(WifiP2pManager.WPS_PBC);
+ }
+ })
+ .setNegativeButton(r.getString(R.string.cancel), null)
+ .create();
+
+ pin.setVisibility(View.GONE);
+ dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
+ peer.deviceAddress));
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void notifyP2pProvDiscPinRequest(WifiP2pDevice peer) {
+ Resources r = Resources.getSystem();
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
+ final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (DBG) Slog.d(TAG, getName() + " wps_pin");
+ sendMessage(WifiP2pManager.WPS_PIN, pin.getText().toString());
+ }
+ })
+ .setNegativeButton(r.getString(R.string.cancel), null)
+ .create();
+
+ dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
+ peer.deviceAddress));
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void notifyP2pInvitationReceived(WifiP2pGroup group) {
+ mSavedP2pGroup = group;
+ Resources r = Resources.getSystem();
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
+ final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
+ config.joinExistingGroup = true;
+ if (DBG) Slog.d(TAG, getName() + " connect to invited group");
+ sendMessage(WifiP2pManager.CONNECT, config);
+ mSavedP2pGroup = null;
+ }
+ })
+ .setNegativeButton(r.getString(R.string.cancel), null)
+ .create();
+
+ pin.setVisibility(View.GONE);
+ dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
+ group.getOwner().deviceAddress));
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ }
+
+ private void updateDeviceStatus(String deviceAddress, Status status) {
+ for (WifiP2pDevice d : mPeers.getDeviceList()) {
+ // TODO: fix later
+ // if (d.deviceAddress.equals(deviceAddress)) {
+ if (d.deviceAddress.startsWith(deviceAddress.substring(0, 8))) {
+ d.status = status;
+ }
+ }
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl
new file mode 100644
index 0000000..7bab5d3
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+parcelable WifiP2pStatus;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java
new file mode 100644
index 0000000..1c9b76c
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A class representing Wi-fi P2p status
+ * @hide
+ */
+public class WifiP2pStatus implements Parcelable {
+
+ //Comes from the wpa_supplicant
+ enum p2p_status_code {
+ SUCCESS,
+ FAIL_INFO_CURRENTLY_UNAVAILABLE,
+ FAIL_INCOMPATIBLE_PARAMS,
+ FAIL_LIMIT_REACHED,
+ FAIL_INVALID_PARAMS,
+ FAIL_UNABLE_TO_ACCOMMODATE,
+ FAIL_PREV_PROTOCOL_ERROR,
+ FAIL_NO_COMMON_CHANNELS,
+ FAIL_UNKNOWN_GROUP,
+ FAIL_BOTH_GO_INTENT_15,
+ FAIL_INCOMPATIBLE_PROV_METHOD,
+ FAIL_REJECTED_BY_USER
+ };
+
+ public WifiP2pStatus() {
+ }
+
+ //TODO: add support
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor {@hide} */
+ //TODO: implement
+ public WifiP2pStatus(WifiP2pStatus source) {
+ if (source != null) {
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ // STOPSHIP: implement
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<WifiP2pStatus> CREATOR =
+ new Creator<WifiP2pStatus>() {
+ public WifiP2pStatus createFromParcel(Parcel in) {
+ WifiP2pStatus status = new WifiP2pStatus();
+ return status;
+ }
+
+ public WifiP2pStatus[] newArray(int size) {
+ return new WifiP2pStatus[size];
+ }
+ };
+}