diff options
148 files changed, 6740 insertions, 4150 deletions
@@ -123,6 +123,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/storage/IMountService.aidl \ core/java/android/os/storage/IMountServiceListener.aidl \ core/java/android/os/storage/IMountShutdownObserver.aidl \ + core/java/android/os/storage/IObbActionListener.aidl \ core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetStatService.aidl \ core/java/android/os/IPermissionController.aidl \ diff --git a/api/current.xml b/api/current.xml index 53d2fdd..64ac66f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -30669,6 +30669,17 @@ <parameter name="holder" type="android.view.SurfaceHolder"> </parameter> </method> +<field name="KEY_NATIVE_SAVED_STATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android:native_state"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="META_DATA_LIB_NAME" type="java.lang.String" transient="false" @@ -136174,6 +136185,8 @@ > <parameter name="filename" type="java.lang.String"> </parameter> +<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> +</exception> </method> <method name="isUsbMassStorageConnected" return="boolean" @@ -136239,6 +136252,8 @@ </parameter> <parameter name="force" type="boolean"> </parameter> +<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> +</exception> </method> <method name="unregisterListener" return="void" diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java index eaf0675..4dc88b3 100644 --- a/core/java/android/app/NativeActivity.java +++ b/core/java/android/app/NativeActivity.java @@ -10,6 +10,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; +import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.Build; import android.os.Bundle; @@ -32,12 +33,27 @@ import java.lang.ref.WeakReference; /** * Convenience for implementing an activity that will be implemented - * purely in native code. That is, a game (or game-like thing). + * purely in native code. That is, a game (or game-like thing). There + * is no need to derive from this class; you can simply declare it in your + * manifest, and use the NDK APIs from there. + * + * <p>A typical manifest would look like: + * + * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml + * manifest} + * + * <p>A very simple example of native code that is run by NativeActivity + * follows. This reads input events from the user and uses OpenGLES to + * draw into the native activity's window. + * + * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all} */ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, InputQueue.Callback, OnGlobalLayoutListener { public static final String META_DATA_LIB_NAME = "android.app.lib_name"; + public static final String KEY_NATIVE_SAVED_STATE = "android:native_state"; + private NativeContentView mNativeContentView; private InputMethodManager mIMM; private InputMethodCallback mInputMethodCallback; @@ -59,14 +75,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private native int loadNativeCode(String path, MessageQueue queue, String internalDataPath, String externalDataPath, int sdkVersion, - AssetManager assetMgr); + AssetManager assetMgr, byte[] savedState); private native void unloadNativeCode(int handle); private native void onStartNative(int handle); private native void onResumeNative(int handle); - private native void onSaveInstanceStateNative(int handle); + private native byte[] onSaveInstanceStateNative(int handle); private native void onPauseNative(int handle); private native void onStopNative(int handle); + private native void onConfigurationChangedNative(int handle); private native void onLowMemoryNative(int handle); private native void onWindowFocusChangedNative(int handle, boolean focused); private native void onSurfaceCreatedNative(int handle, Surface surface); @@ -165,10 +182,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, throw new IllegalArgumentException("Unable to find native library: " + libname); } + byte[] nativeSavedState = savedInstanceState != null + ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null; + mNativeHandle = loadNativeCode(path, Looper.myQueue(), getFilesDir().toString(), Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(), - Build.VERSION.SDK_INT, getAssets()); + Build.VERSION.SDK_INT, getAssets(), nativeSavedState); if (mNativeHandle == 0) { throw new IllegalArgumentException("Unable to load native library: " + path); @@ -206,7 +226,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - onSaveInstanceStateNative(mNativeHandle); + byte[] state = onSaveInstanceStateNative(mNativeHandle); + if (state != null) { + outState.putByteArray(KEY_NATIVE_SAVED_STATE, state); + } } @Override @@ -222,6 +245,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (!mDestroyed) { + onConfigurationChangedNative(mNativeHandle); + } + } + + @Override public void onLowMemory() { super.onLowMemory(); if (!mDestroyed) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ba1b3a9..812af5a 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1333,7 +1333,7 @@ public class Resources { height = mMetrics.widthPixels; } int keyboardHidden = mConfiguration.keyboardHidden; - if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO + if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) { keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT; diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 11cd526..efbccd2 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -526,12 +526,6 @@ public class MobileDataStateTracker implements NetworkStateTracker { return -1; } - /** - * This is not supported. - */ - public void interpretScanResultsAvailable() { - } - @Override public String toString() { StringBuffer sb = new StringBuffer("Mobile data state: "); diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index 44215e7..82735e5 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -27,18 +27,17 @@ package android.net; public interface NetworkStateTracker { public static final int EVENT_STATE_CHANGED = 1; - public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2; /** * arg1: 1 to show, 0 to hide * arg2: ID of the notification * obj: Notification (if showing) */ - public static final int EVENT_NOTIFICATION_CHANGED = 3; - public static final int EVENT_CONFIGURATION_CHANGED = 4; - public static final int EVENT_ROAMING_CHANGED = 5; - public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6; - public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7; - public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8; + public static final int EVENT_NOTIFICATION_CHANGED = 2; + public static final int EVENT_CONFIGURATION_CHANGED = 3; + public static final int EVENT_ROAMING_CHANGED = 4; + public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 5; + public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6; + public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7; /** * Fetch NetworkInfo for the network @@ -147,10 +146,4 @@ public interface NetworkStateTracker { */ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid); - /** - * Interprets scan results. This will be called at a safe time for - * processing, and from a safe thread. - */ - public void interpretScanResultsAvailable(); - } diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl index ca7efe7..5c69214 100644 --- a/core/java/android/os/storage/IMountService.aidl +++ b/core/java/android/os/storage/IMountService.aidl @@ -19,6 +19,7 @@ package android.os.storage; import android.os.storage.IMountServiceListener; import android.os.storage.IMountShutdownObserver; +import android.os.storage.IObbActionListener; /** WARNING! Update IMountService.h and IMountService.cpp if you change this file. * In particular, the ordering of the methods below must match the @@ -156,14 +157,20 @@ interface IMountService /** * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only * allows the calling process's UID access to the contents. + * + * MountService will call back to the supplied IObbActionListener to inform + * it of the terminal state of the call. */ - int mountObb(String filename, String key); + void mountObb(String filename, String key, IObbActionListener token); /** * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any * program using it will be forcibly killed to unmount the image. + * + * MountService will call back to the supplied IObbActionListener to inform + * it of the terminal state of the call. */ - int unmountObb(String filename, boolean force); + void unmountObb(String filename, boolean force, IObbActionListener token); /** * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere. diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl new file mode 100644 index 0000000..78d7a9e --- /dev/null +++ b/core/java/android/os/storage/IObbActionListener.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 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.os.storage; + +/** + * Callback class for receiving events from MountService about + * Opaque Binary Blobs (OBBs). + * + * @hide - Applications should use android.os.storage.StorageManager + * to interact with OBBs. + */ +interface IObbActionListener { + /** + * Return from an OBB action result. + * + * @param filename the path to the OBB the operation was performed on + * @param returnCode status of the operation + */ + void onObbResult(String filename, String status); +} diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 61cdace..7c9effa 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -16,28 +16,14 @@ package android.os.storage; -import android.content.Context; -import android.os.Binder; -import android.os.Bundle; -import android.os.Looper; -import android.os.Parcelable; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.os.RemoteException; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.os.ServiceManager; -import android.os.storage.IMountService; -import android.os.storage.IMountServiceListener; import android.util.Log; -import android.util.SparseArray; -import java.io.FileDescriptor; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; /** * StorageManager is the interface to the systems storage service. @@ -88,6 +74,17 @@ public class StorageManager } /** + * Binder listener for OBB action results. + */ + private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener(); + private class ObbActionBinderListener extends IObbActionListener.Stub { + @Override + public void onObbResult(String filename, String status) throws RemoteException { + Log.i(TAG, "filename = " + filename + ", result = " + status); + } + } + + /** * Private base class for messages sent between the callback thread * and the target looper handler. */ @@ -290,12 +287,23 @@ public class StorageManager } /** - * Mount an OBB file. + * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is + * specified, it is supplied to the mounting process to be used in any + * encryption used in the OBB. + * <p> + * <em>Note:</em> you can only mount OBB files for which the OBB tag on the + * file matches a package ID that is owned by the calling program's UID. + * That is, shared UID applications can obtain access to any other + * application's OBB that shares its UID. + * + * @param filename the path to the OBB file + * @param key decryption key + * @return whether the mount call was successfully queued or not */ public boolean mountObb(String filename, String key) { try { - return mMountService.mountObb(filename, key) - == StorageResultCode.OperationSucceeded; + mMountService.mountObb(filename, key, mObbActionListener); + return true; } catch (RemoteException e) { Log.e(TAG, "Failed to mount OBB", e); } @@ -304,12 +312,24 @@ public class StorageManager } /** - * Mount an OBB file. + * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag + * is true, it will kill any application needed to unmount the given OBB. + * <p> + * <em>Note:</em> you can only mount OBB files for which the OBB tag on the + * file matches a package ID that is owned by the calling program's UID. + * That is, shared UID applications can obtain access to any other + * application's OBB that shares its UID. + * + * @param filename path to the OBB file + * @param force whether to kill any programs using this in order to unmount + * it + * @return whether the unmount call was successfully queued or not + * @throws IllegalArgumentException when OBB is not already mounted */ - public boolean unmountObb(String filename, boolean force) { + public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException { try { - return mMountService.unmountObb(filename, force) - == StorageResultCode.OperationSucceeded; + mMountService.unmountObb(filename, force, mObbActionListener); + return true; } catch (RemoteException e) { Log.e(TAG, "Failed to mount OBB", e); } @@ -317,7 +337,13 @@ public class StorageManager return false; } - public boolean isObbMounted(String filename) { + /** + * Check whether an Opaque Binary Blob (OBB) is mounted or not. + * + * @param filename path to OBB image + * @return true if OBB is mounted; false if not mounted or on error + */ + public boolean isObbMounted(String filename) throws IllegalArgumentException { try { return mMountService.isObbMounted(filename); } catch (RemoteException e) { @@ -328,13 +354,21 @@ public class StorageManager } /** - * Check the mounted path of an OBB file. + * Check the mounted path of an Opaque Binary Blob (OBB) file. This will + * give you the path to where you can obtain access to the internals of the + * OBB. + * + * @param filename path to OBB image + * @return absolute path to mounted OBB image data or <code>null</code> if + * not mounted or exception encountered trying to read status */ public String getMountedObbPath(String filename) { try { return mMountService.getMountedObbPath(filename); } catch (RemoteException e) { Log.e(TAG, "Failed to find mounted path for OBB", e); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Couldn't read OBB file", e); } return null; diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index d75d421..d7b4452 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -772,6 +772,9 @@ public class WebViewDatabase { } long getCacheTotalSize() { + if (mCacheDatabase == null) { + return 0; + } long size = 0; Cursor cursor = null; final String query = "SELECT SUM(contentlength) as sum FROM cache"; diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 89649a9..5d1f632 100755 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -19,6 +19,7 @@ package com.android.internal.app; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.content.pm.PackageInfoLite; +import android.content.res.ObbInfo; interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, @@ -28,4 +29,5 @@ interface IMediaContainerService { in ParcelFileDescriptor outStream); PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags); boolean checkFreeStorage(boolean external, in Uri fileUri); + ObbInfo getObbInfo(String filename); } diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java new file mode 100644 index 0000000..ce5959d --- /dev/null +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.ImageView; + +public class PlatLogoActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ImageView content = new ImageView(this); + content.setImageResource(com.android.internal.R.drawable.platlogo); + content.setScaleType(ImageView.ScaleType.FIT_CENTER); + + setContentView(content); + } +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index d1a5ae1..860b5b7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -139,7 +139,8 @@ LOCAL_SRC_FILES:= \ android_backup_BackupDataOutput.cpp \ android_backup_FileBackupHelperBase.cpp \ android_backup_BackupHelperDispatcher.cpp \ - android_content_res_ObbScanner.cpp + android_content_res_ObbScanner.cpp \ + android_content_res_Configuration.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 8b09e5f..2dd17bb 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -167,6 +167,7 @@ extern int register_android_view_InputQueue(JNIEnv* env); extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); +extern int register_android_content_res_Configuration(JNIEnv* env); static AndroidRuntime* gCurRuntime = NULL; @@ -1331,6 +1332,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_MotionEvent), REG_JNI(register_android_content_res_ObbScanner), + REG_JNI(register_android_content_res_Configuration), }; /* diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 1feb3b3..0932473 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -600,7 +600,7 @@ static bool mainWorkCallback(int fd, int events, void* data) { static jint loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue, jstring internalDataDir, jstring externalDataDir, int sdkVersion, - jobject jAssetMgr) + jobject jAssetMgr, jbyteArray savedState) { LOG_TRACE("loadNativeCode_native"); @@ -666,7 +666,18 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ code->assetManager = assetManagerForJavaObject(env, jAssetMgr); - code->createActivityFunc(code, NULL, 0); + jbyte* rawSavedState = NULL; + jsize rawSavedSize = 0; + if (savedState != NULL) { + rawSavedState = env->GetByteArrayElements(savedState, NULL); + rawSavedSize = env->GetArrayLength(savedState); + } + + code->createActivityFunc(code, rawSavedState, rawSavedSize); + + if (rawSavedState != NULL) { + env->ReleaseByteArrayElements(savedState, rawSavedState, 0); + } } return (jint)code; @@ -706,17 +717,31 @@ onResume_native(JNIEnv* env, jobject clazz, jint handle) } } -static void +static jbyteArray onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) { LOG_TRACE("onSaveInstanceState_native"); + + jbyteArray array = NULL; + if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onSaveInstanceState != NULL) { size_t len = 0; - code->callbacks.onSaveInstanceState(code, &len); + jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len); + if (len > 0) { + array = env->NewByteArray(len); + if (array != NULL) { + env->SetByteArrayRegion(array, 0, len, state); + } + } + if (state != NULL) { + free(state); + } } } + + return array; } static void @@ -744,6 +769,18 @@ onStop_native(JNIEnv* env, jobject clazz, jint handle) } static void +onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle) +{ + LOG_TRACE("onConfigurationChanged_native"); + if (handle != 0) { + NativeCode* code = (NativeCode*)handle; + if (code->callbacks.onConfigurationChanged != NULL) { + code->callbacks.onConfigurationChanged(code); + } + } +} + +static void onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) { LOG_TRACE("onLowMemory_native"); @@ -934,14 +971,15 @@ finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, } static const JNINativeMethod g_methods[] = { - { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I", + { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I", (void*)loadNativeCode_native }, { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, { "onStartNative", "(I)V", (void*)onStart_native }, { "onResumeNative", "(I)V", (void*)onResume_native }, - { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native }, + { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native }, { "onPauseNative", "(I)V", (void*)onPause_native }, { "onStopNative", "(I)V", (void*)onStop_native }, + { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native }, { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp new file mode 100644 index 0000000..28a43ab --- /dev/null +++ b/core/jni/android_content_res_Configuration.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2010, 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. + */ + +#define LOG_TAG "Configuration" + +#include <utils/Log.h> +#include "utils/misc.h" + +#include "jni.h" +#include <android_runtime/android_content_res_Configuration.h> +#include "android_runtime/AndroidRuntime.h" + +namespace android { + +static struct { + jclass clazz; + + jfieldID mcc; + jfieldID mnc; + jfieldID locale; + jfieldID screenLayout; + jfieldID touchscreen; + jfieldID keyboard; + jfieldID keyboardHidden; + jfieldID hardKeyboardHidden; + jfieldID navigation; + jfieldID navigationHidden; + jfieldID orientation; + jfieldID uiMode; +} gConfigurationClassInfo; + +void android_Configuration_getFromJava( + JNIEnv* env, jobject clazz, struct AConfiguration* out) { + out->mcc = env->GetIntField(clazz, gConfigurationClassInfo.mcc); + out->mnc = env->GetIntField(clazz, gConfigurationClassInfo.mnc); + out->screenLayout = env->GetIntField(clazz, gConfigurationClassInfo.screenLayout); + out->touchscreen = env->GetIntField(clazz, gConfigurationClassInfo.touchscreen); + out->keyboard = env->GetIntField(clazz, gConfigurationClassInfo.keyboard); + out->navigation = env->GetIntField(clazz, gConfigurationClassInfo.navigation); + + out->inputFlags = env->GetIntField(clazz, gConfigurationClassInfo.keyboardHidden); + int hardKeyboardHidden = env->GetIntField(clazz, gConfigurationClassInfo.hardKeyboardHidden); + if (out->inputFlags == ACONFIGURATION_KEYSHIDDEN_NO + && hardKeyboardHidden == 2) { + out->inputFlags = ACONFIGURATION_KEYSHIDDEN_SOFT; + } + out->inputFlags |= env->GetIntField(clazz, gConfigurationClassInfo.navigationHidden) + << ResTable_config::SHIFT_NAVHIDDEN; + + out->orientation = env->GetIntField(clazz, gConfigurationClassInfo.orientation); + out->uiMode = env->GetIntField(clazz, gConfigurationClassInfo.uiMode); +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z", + // (void*) android_content_res_ObbScanner_getObbInfo }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_content_res_Configuration(JNIEnv* env) +{ + FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration"); + + GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz, + "mcc", "I"); + GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz, + "mnc", "I"); + GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz, + "locale", "Ljava/util/Locale;"); + GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz, + "screenLayout", "I"); + GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz, + "touchscreen", "I"); + GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz, + "keyboard", "I"); + GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz, + "keyboardHidden", "I"); + GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz, + "hardKeyboardHidden", "I"); + GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz, + "navigation", "I"); + GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz, + "navigationHidden", "I"); + GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz, + "orientation", "I"); + GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz, + "uiMode", "I"); + + return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods, + NELEM(gMethods)); +} + +}; // namespace android diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 01ded68..1f66d05 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1284,6 +1284,9 @@ android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> + <activity android:name="com.android.internal.app.PlatLogoActivity" + android:theme="@style/Theme.NoTitleBar.Fullscreen"> + </activity> <activity android:name="com.android.internal.app.DisableCarModeActivity" android:theme="@style/Theme.NoDisplay" android:excludeFromRecents="true"> diff --git a/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png Binary files differnew file mode 100644 index 0000000..97e6806 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png Binary files differindex aad9ef7..911e1aa 100644 --- a/core/res/res/drawable-hdpi/btn_check_off.png +++ b/core/res/res/drawable-hdpi/btn_check_off.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png Binary files differindex eaee9e0..d72e2b9 100644 --- a/core/res/res/drawable-hdpi/btn_check_off_disable.png +++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png Binary files differindex 6d2c293..d72e2b9 100644 --- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png +++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png Binary files differnew file mode 100644 index 0000000..240a044 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png Binary files differnew file mode 100644 index 0000000..240a044 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_light.png b/core/res/res/drawable-hdpi/btn_check_off_light.png Binary files differnew file mode 100644 index 0000000..4ca3c56 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_off_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png Binary files differindex 1c442e9..08f4181 100644 --- a/core/res/res/drawable-hdpi/btn_check_off_pressed.png +++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png Binary files differnew file mode 100644 index 0000000..d3754dd --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png Binary files differindex b852b2c..264f102 100644 --- a/core/res/res/drawable-hdpi/btn_check_off_selected.png +++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png Binary files differnew file mode 100644 index 0000000..48506bf --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png Binary files differindex cd5c181..5541c67 100644 --- a/core/res/res/drawable-hdpi/btn_check_on.png +++ b/core/res/res/drawable-hdpi/btn_check_on.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png Binary files differindex b4fc51a..7805458 100644 --- a/core/res/res/drawable-hdpi/btn_check_on_disable.png +++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png Binary files differindex bf34647..7805458 100644 --- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png +++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png Binary files differnew file mode 100644 index 0000000..4e268d5 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png Binary files differnew file mode 100644 index 0000000..4e268d5 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_light.png b/core/res/res/drawable-hdpi/btn_check_on_light.png Binary files differnew file mode 100644 index 0000000..768c4af --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_on_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png Binary files differindex fa5c7a2..37e3953 100644 --- a/core/res/res/drawable-hdpi/btn_check_on_pressed.png +++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png Binary files differnew file mode 100644 index 0000000..fc29e46 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png Binary files differindex a6a21ad..a0beac4 100644 --- a/core/res/res/drawable-hdpi/btn_check_on_selected.png +++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png Binary files differnew file mode 100644 index 0000000..5df45c7 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png Binary files differnew file mode 100644 index 0000000..45c5c6a --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png Binary files differindex c0b14aa..301c97d 100644 --- a/core/res/res/drawable-hdpi/btn_radio_off.png +++ b/core/res/res/drawable-hdpi/btn_radio_off.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off_light.png b/core/res/res/drawable-hdpi/btn_radio_off_light.png Binary files differnew file mode 100644 index 0000000..657c8e5 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_off_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png Binary files differindex 3189581..5e6ef2b 100644 --- a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png +++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png Binary files differnew file mode 100644 index 0000000..342bf11 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png Binary files differindex f337703..d11ae85 100644 --- a/core/res/res/drawable-hdpi/btn_radio_off_selected.png +++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png Binary files differnew file mode 100644 index 0000000..68bd1df --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png Binary files differindex c90d2eb..5b0dbe8 100644 --- a/core/res/res/drawable-hdpi/btn_radio_on.png +++ b/core/res/res/drawable-hdpi/btn_radio_on.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on_light.png b/core/res/res/drawable-hdpi/btn_radio_on_light.png Binary files differnew file mode 100644 index 0000000..45ae36b --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_on_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png Binary files differindex d79450b..c3a0d48 100644 --- a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png +++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png Binary files differnew file mode 100644 index 0000000..ca22358 --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png Binary files differindex db50c43..6c05f47 100644 --- a/core/res/res/drawable-hdpi/btn_radio_on_selected.png +++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png Binary files differnew file mode 100644 index 0000000..a17fa1e --- /dev/null +++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png Binary files differnew file mode 100644 index 0000000..79367b8 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png Binary files differindex 56d3861..5e44c29 100644 --- a/core/res/res/drawable-mdpi/btn_check_off.png +++ b/core/res/res/drawable-mdpi/btn_check_off.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png Binary files differindex e012afd..a603fb1 100644 --- a/core/res/res/drawable-mdpi/btn_check_off_disable.png +++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png Binary files differindex 0837bbd..a603fb1 100644 --- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png +++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png Binary files differnew file mode 100644 index 0000000..69e9ff9 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png Binary files differnew file mode 100644 index 0000000..69e9ff9 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_light.png b/core/res/res/drawable-mdpi/btn_check_off_light.png Binary files differnew file mode 100644 index 0000000..5b2ec92 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_off_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png Binary files differindex 984dfd7..611bb1d 100644 --- a/core/res/res/drawable-mdpi/btn_check_off_pressed.png +++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png Binary files differnew file mode 100644 index 0000000..5a0ea44 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png Binary files differindex 20842d4..aa28df2 100644 --- a/core/res/res/drawable-mdpi/btn_check_off_selected.png +++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png Binary files differnew file mode 100644 index 0000000..ade1136 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png Binary files differindex 791ac1d..130d562 100644 --- a/core/res/res/drawable-mdpi/btn_check_on.png +++ b/core/res/res/drawable-mdpi/btn_check_on.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png Binary files differindex 6cb02f3..f19972a 100644 --- a/core/res/res/drawable-mdpi/btn_check_on_disable.png +++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png Binary files differindex 8a73b33..f19972a 100644 --- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png +++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png Binary files differnew file mode 100644 index 0000000..13ef46e --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png Binary files differnew file mode 100644 index 0000000..13ef46e --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_light.png b/core/res/res/drawable-mdpi/btn_check_on_light.png Binary files differnew file mode 100644 index 0000000..6b7808b --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_on_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png Binary files differindex 300d64a..df753f5 100644 --- a/core/res/res/drawable-mdpi/btn_check_on_pressed.png +++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png Binary files differnew file mode 100644 index 0000000..6a4dd2c --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png Binary files differindex 0b36adb..7586881 100644 --- a/core/res/res/drawable-mdpi/btn_check_on_selected.png +++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png Binary files differnew file mode 100644 index 0000000..24701ce --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png Binary files differnew file mode 100644 index 0000000..16e8939 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png Binary files differindex 407632b..16c1c6b 100644 --- a/core/res/res/drawable-mdpi/btn_radio_off.png +++ b/core/res/res/drawable-mdpi/btn_radio_off.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off_light.png b/core/res/res/drawable-mdpi/btn_radio_off_light.png Binary files differnew file mode 100644 index 0000000..e8287f3 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_off_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png Binary files differindex d6d8a9d..b25217b 100644 --- a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png +++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png Binary files differnew file mode 100644 index 0000000..b63b9b0 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png Binary files differindex 53f3e87..bef7572 100644 --- a/core/res/res/drawable-mdpi/btn_radio_off_selected.png +++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png Binary files differnew file mode 100644 index 0000000..af754e1 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png Binary files differindex 25a3ccc..4ed7471 100644 --- a/core/res/res/drawable-mdpi/btn_radio_on.png +++ b/core/res/res/drawable-mdpi/btn_radio_on.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on_light.png b/core/res/res/drawable-mdpi/btn_radio_on_light.png Binary files differnew file mode 100644 index 0000000..62aaa41 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_on_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png Binary files differindex c904a35..7cf91c6 100644 --- a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png +++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png Binary files differnew file mode 100644 index 0000000..0d93507 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png Binary files differindex 78e1fc0..56f6f5b 100644 --- a/core/res/res/drawable-mdpi/btn_radio_on_selected.png +++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png Binary files differnew file mode 100644 index 0000000..48dd8e9 --- /dev/null +++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg Binary files differnew file mode 100644 index 0000000..0e7780c --- /dev/null +++ b/core/res/res/drawable-nodpi/platlogo.jpg diff --git a/core/res/res/drawable/btn_check_light.xml b/core/res/res/drawable/btn_check_light.xml new file mode 100644 index 0000000..85f119a --- /dev/null +++ b/core/res/res/drawable/btn_check_light.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- Enabled states --> + + <item android:state_checked="true" android:state_window_focused="false" + android:state_enabled="true" + android:drawable="@drawable/btn_check_on_light" /> + <item android:state_checked="false" android:state_window_focused="false" + android:state_enabled="true" + android:drawable="@drawable/btn_check_off_light" /> + + <item android:state_checked="true" android:state_pressed="true" + android:state_enabled="true" + android:drawable="@drawable/btn_check_on_pressed_light" /> + <item android:state_checked="false" android:state_pressed="true" + android:state_enabled="true" + android:drawable="@drawable/btn_check_off_pressed_light" /> + + <item android:state_checked="true" android:state_focused="true" + android:state_enabled="true" + android:drawable="@drawable/btn_check_on_selected_light" /> + <item android:state_checked="false" android:state_focused="true" + android:state_enabled="true" + android:drawable="@drawable/btn_check_off_selected_light" /> + + <item android:state_checked="false" + android:state_enabled="true" + android:drawable="@drawable/btn_check_off_light" /> + <item android:state_checked="true" + android:state_enabled="true" + android:drawable="@drawable/btn_check_on_light" /> + + + <!-- Disabled states --> + + <item android:state_checked="true" android:state_window_focused="false" + android:drawable="@drawable/btn_check_on_disable_light" /> + <item android:state_checked="false" android:state_window_focused="false" + android:drawable="@drawable/btn_check_off_disable_light" /> + + <item android:state_checked="true" android:state_focused="true" + android:drawable="@drawable/btn_check_on_disable_focused_light" /> + <item android:state_checked="false" android:state_focused="true" + android:drawable="@drawable/btn_check_off_disable_focused_light" /> + + <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_light" /> + <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_light" /> + +</selector> diff --git a/core/res/res/drawable/btn_radio_light.xml b/core/res/res/drawable/btn_radio_light.xml new file mode 100644 index 0000000..51c930b --- /dev/null +++ b/core/res/res/drawable/btn_radio_light.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:state_window_focused="false" + android:drawable="@drawable/btn_radio_on_light" /> + <item android:state_checked="false" android:state_window_focused="false" + android:drawable="@drawable/btn_radio_off_light" /> + + <item android:state_checked="true" android:state_pressed="true" + android:drawable="@drawable/btn_radio_on_pressed_light" /> + <item android:state_checked="false" android:state_pressed="true" + android:drawable="@drawable/btn_radio_off_pressed_light" /> + + <item android:state_checked="true" android:state_focused="true" + android:drawable="@drawable/btn_radio_on_selected_light" /> + <item android:state_checked="false" android:state_focused="true" + android:drawable="@drawable/btn_radio_off_selected_light" /> + + <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_light" /> + <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_light" /> +</selector> diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml index 40e423e..ff7df7c 100644 --- a/core/res/res/values-xlarge/styles.xml +++ b/core/res/res/values-xlarge/styles.xml @@ -19,7 +19,6 @@ <style name="TextAppearance.StatusBar"> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> - <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> </style> <style name="TextAppearance.StatusBar.Ticker"> </style> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 993048d..e0dc3a9 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -279,11 +279,21 @@ <item name="android:button">@android:drawable/btn_check</item> </style> + <style name="Widget.CompoundButton.CheckBox.Inverse"> + <item name="android:background">@android:drawable/btn_check_label_background_light</item> + <item name="android:button">@android:drawable/btn_check_light</item> + </style> + <style name="Widget.CompoundButton.RadioButton"> <item name="android:background">@android:drawable/btn_radio_label_background</item> <item name="android:button">@android:drawable/btn_radio</item> </style> + <style name="Widget.CompoundButton.RadioButton.Inverse"> + <item name="android:background">@android:drawable/btn_radio_label_background_light</item> + <item name="android:button">@android:drawable/btn_radio_light</item> + </style> + <style name="Widget.CompoundButton.Star"> <item name="android:background">@android:drawable/btn_star_label_background</item> <item name="android:button">@android:drawable/btn_star</item> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 739912a..3348b4e 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -84,9 +84,9 @@ <item name="searchResultListItemHeight">58dip</item> <item name="listDivider">@drawable/divider_horizontal_dark</item> <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item> - - <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item> - <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item> + + <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item> + <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item> <item name="expandableListPreferredItemPaddingLeft">40dip</item> <item name="expandableListPreferredChildPaddingLeft"> @@ -160,8 +160,8 @@ <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item> <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item> <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item> - <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item> - <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> <item name="seekBarStyle">@android:style/Widget.SeekBar</item> <item name="ratingBarStyle">@android:style/Widget.RatingBar</item> <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item> @@ -253,18 +253,24 @@ <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item> <item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item> + <!-- List attributes --> + <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_light</item> + <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_light</item> + + <!-- Widget styles --> + <item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox.Inverse</item> <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item> <item name="expandableListViewStyle">@android:style/Widget.ExpandableListView.White</item> <item name="listViewStyle">@android:style/Widget.ListView.White</item> <item name="listDivider">@drawable/divider_horizontal_bright</item> <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item> - <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item> - <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item> - <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item> - <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item> - <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item> - <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> + <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item> + <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item> + <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item> + <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item> + <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> + <item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton.Inverse</item> </style> <!-- Variant of the light theme with no title bar --> diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java index e854cd9..5ffc21a 100644 --- a/graphics/java/android/renderscript/Matrix4f.java +++ b/graphics/java/android/renderscript/Matrix4f.java @@ -179,6 +179,76 @@ public class Matrix4f { tmp.loadTranslate(x, y, z); multiply(tmp); } + private float computeCofactor(int i, int j) { + int c0 = (i+1) % 4; + int c1 = (i+2) % 4; + int c2 = (i+3) % 4; + int r0 = (j+1) % 4; + int r1 = (j+2) % 4; + int r2 = (j+3) % 4; + + float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] - + mMat[c1 + 4*r2] * mMat[c2 + 4*r1])) + - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] - + mMat[c1 + 4*r2] * mMat[c2 + 4*r0])) + + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] - + mMat[c1 + 4*r1] * mMat[c2 + 4*r0])); + + float cofactor = ((i+j) & 1) != 0 ? -minor : minor; + return cofactor; + } + + public boolean inverse() { + + Matrix4f result = new Matrix4f(); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + result.mMat[4*i + j] = computeCofactor(i, j); + } + } + + // Dot product of 0th column of source and 0th row of result + float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] + + mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3]; + + if (Math.abs(det) < 1e-6) { + return false; + } + + det = 1.0f / det; + for (int i = 0; i < 16; ++i) { + mMat[i] = result.mMat[i] * det; + } + + return true; + } + + public boolean inverseTranspose() { + + Matrix4f result = new Matrix4f(); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + result.mMat[4*j + i] = computeCofactor(i, j); + } + } + + float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] + + mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12]; + + if (Math.abs(det) < 1e-6) { + return false; + } + + det = 1.0f / det; + for (int i = 0; i < 16; ++i) { + mMat[i] = result.mMat[i] * det; + } + + return true; + } + public void transpose() { for(int i = 0; i < 3; ++i) { for(int j = i + 1; j < 4; ++j) { diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h new file mode 100644 index 0000000..2f5a982 --- /dev/null +++ b/include/android_runtime/android_content_res_Configuration.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H +#define _ANDROID_CONTENT_RES_CONFIGURATION_H + +#include <utils/ResourceTypes.h> +#include <android/configuration.h> + +#include "jni.h" + +struct AConfiguration : android::ResTable_config { +}; + +namespace android { + +extern void android_Configuration_getFromJava( + JNIEnv* env, jobject clazz, struct AConfiguration* out); + +} // namespace android + + +#endif // _ANDROID_CONTENT_RES_CONFIGURATION_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index 97694ff..9e2bf37 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -129,6 +129,8 @@ public: */ void setConfiguration(const ResTable_config& config, const char* locale = NULL); + void getConfiguration(ResTable_config* outConfig) const; + typedef Asset::AccessMode AccessMode; // typing shortcut /* diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h index 075927c..d2ca82e 100644 --- a/include/utils/ObbFile.h +++ b/include/utils/ObbFile.h @@ -35,6 +35,8 @@ public: bool readFrom(int fd); bool writeTo(const char* filename); bool writeTo(int fd); + bool removeFrom(const char* filename); + bool removeFrom(int fd); const char* getFileName() const { return mFileName; @@ -78,6 +80,8 @@ private: size_t mFileSize; + size_t mFooterStart; + unsigned char* mReadBuf; bool parseObbFile(int fd); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index c7d9ff1..da86da4 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -31,6 +31,8 @@ #include <stdint.h> #include <sys/types.h> +#include <android/configuration.h> + namespace android { /** ******************************************************************** @@ -822,25 +824,25 @@ struct ResTable_config }; enum { - ORIENTATION_ANY = 0x0000, - ORIENTATION_PORT = 0x0001, - ORIENTATION_LAND = 0x0002, - ORIENTATION_SQUARE = 0x0003, + ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY, + ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT, + ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND, + ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE, }; enum { - TOUCHSCREEN_ANY = 0x0000, - TOUCHSCREEN_NOTOUCH = 0x0001, - TOUCHSCREEN_STYLUS = 0x0002, - TOUCHSCREEN_FINGER = 0x0003, + TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY, + TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH, + TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS, + TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER, }; enum { - DENSITY_DEFAULT = 0, - DENSITY_LOW = 120, - DENSITY_MEDIUM = 160, - DENSITY_HIGH = 240, - DENSITY_NONE = 0xffff + DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT, + DENSITY_LOW = ACONFIGURATION_DENSITY_LOW, + DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, + DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, + DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; union { @@ -853,33 +855,34 @@ struct ResTable_config }; enum { - KEYBOARD_ANY = 0x0000, - KEYBOARD_NOKEYS = 0x0001, - KEYBOARD_QWERTY = 0x0002, - KEYBOARD_12KEY = 0x0003, + KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY, + KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS, + KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY, + KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY, }; enum { - NAVIGATION_ANY = 0x0000, - NAVIGATION_NONAV = 0x0001, - NAVIGATION_DPAD = 0x0002, - NAVIGATION_TRACKBALL = 0x0003, - NAVIGATION_WHEEL = 0x0004, + NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY, + NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV, + NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD, + NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL, + NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL, }; enum { MASK_KEYSHIDDEN = 0x0003, - KEYSHIDDEN_ANY = 0x0000, - KEYSHIDDEN_NO = 0x0001, - KEYSHIDDEN_YES = 0x0002, - KEYSHIDDEN_SOFT = 0x0003, + KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY, + KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO, + KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES, + KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT, }; enum { MASK_NAVHIDDEN = 0x000c, - NAVHIDDEN_ANY = 0x0000, - NAVHIDDEN_NO = 0x0004, - NAVHIDDEN_YES = 0x0008, + SHIFT_NAVHIDDEN = 2, + NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN, + NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN, + NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN, }; union { @@ -929,32 +932,34 @@ struct ResTable_config enum { // screenLayout bits for screen size class. MASK_SCREENSIZE = 0x0f, - SCREENSIZE_ANY = 0x00, - SCREENSIZE_SMALL = 0x01, - SCREENSIZE_NORMAL = 0x02, - SCREENSIZE_LARGE = 0x03, - SCREENSIZE_XLARGE = 0x04, + SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY, + SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL, + SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL, + SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE, + SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE, // screenLayout bits for wide/long screen variation. MASK_SCREENLONG = 0x30, - SCREENLONG_ANY = 0x00, - SCREENLONG_NO = 0x10, - SCREENLONG_YES = 0x20, + SHIFT_SCREENLONG = 4, + SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG, + SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG, + SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG, }; enum { // uiMode bits for the mode type. MASK_UI_MODE_TYPE = 0x0f, - UI_MODE_TYPE_ANY = 0x00, - UI_MODE_TYPE_NORMAL = 0x01, - UI_MODE_TYPE_DESK = 0x02, - UI_MODE_TYPE_CAR = 0x03, + UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY, + UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, + UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, + UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, - UI_MODE_NIGHT_ANY = 0x00, - UI_MODE_NIGHT_NO = 0x10, - UI_MODE_NIGHT_YES = 0x20, + SHIFT_UI_MODE_NIGHT = 4, + UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT, }; union { @@ -1023,19 +1028,19 @@ struct ResTable_config // match the corresponding ones in android.content.pm.ActivityInfo and // attrs_manifest.xml. enum { - CONFIG_MCC = 0x0001, - CONFIG_MNC = 0x0002, - CONFIG_LOCALE = 0x0004, - CONFIG_TOUCHSCREEN = 0x0008, - CONFIG_KEYBOARD = 0x0010, - CONFIG_KEYBOARD_HIDDEN = 0x0020, - CONFIG_NAVIGATION = 0x0040, - CONFIG_ORIENTATION = 0x0080, - CONFIG_DENSITY = 0x0100, - CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400, - CONFIG_SCREEN_LAYOUT = 0x0800, - CONFIG_UI_MODE = 0x1000 + CONFIG_MCC = ACONFIGURATION_MCC, + CONFIG_MNC = ACONFIGURATION_MCC, + CONFIG_LOCALE = ACONFIGURATION_LOCALE, + CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN, + CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD, + CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN, + CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION, + CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION, + CONFIG_DENSITY = ACONFIGURATION_DENSITY, + CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE, + CONFIG_VERSION = ACONFIGURATION_VERSION, + CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT, + CONFIG_UI_MODE = ACONFIGURATION_UI_MODE }; // Compare two configuration, returning CONFIG_* flags set for each value diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index d62fa55..7d31bd6 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -391,7 +391,8 @@ Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) alloc->setName(name.string(), name.size()); // Read in all of our allocation data - stream->loadByteArray(alloc->getPtr(), dataSize); + alloc->data(stream->getPtr() + stream->getPos(), dataSize); + stream->reset(stream->getPos() + dataSize); return alloc; } diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 05902f9..5dee1fb 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -114,6 +114,7 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) Element *elem = new Element(rsc); elem->mComponent.loadFromStream(stream); elem->mBits = elem->mComponent.getBits(); + elem->mHasReference = elem->mComponent.isReference(); elem->mFieldCount = stream->loadU32(); if(elem->mFieldCount) { @@ -125,6 +126,10 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) elem->mFields[ct].e.set(fieldElem); elem->mFields[ct].offsetBits = offset; offset += fieldElem->getSizeBits(); + // Check if our sub-elements have references + if(fieldElem->mHasReference) { + elem->mHasReference = true; + } } } diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh index 85f3b25..aa9aebc 100644 --- a/libs/rs/scriptc/rs_core.rsh +++ b/libs/rs/scriptc/rs_core.rsh @@ -602,6 +602,171 @@ rsMatrixTranspose(rs_matrix2x2 *m) { m->m[2] = temp; } +///////////////////////////////////////////////////// +// quaternion ops +///////////////////////////////////////////////////// + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) { + q->w = w; + q->x = x; + q->y = y; + q->z = z; +} + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = rhs->w; + q->x = rhs->x; + q->y = rhs->y; + q->z = rhs->z; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, float s) { + q->w *= s; + q->x *= s; + q->y *= s; + q->z *= s; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w; + q->x = q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x; + q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->z + q->w*rhs->y; + q->z = q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z; +} + +static void +rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) { + q->w *= rhs->w; + q->x *= rhs->x; + q->y *= rhs->y; + q->z *= rhs->z; +} + +static void +rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) { + rot *= (float)(M_PI / 180.0f) * 0.5f; + float c = cos(rot); + float s = sin(rot); + + q->w = c; + q->x = x * s; + q->y = y * s; + q->z = z * s; +} + +static void +rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) { + const float len = x*x + y*y + z*z; + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + rsQuaternionLoadRotateUnit(q, rot, x, y, z); +} + +static void +rsQuaternionConjugate(rs_quaternion *q) { + q->x = -q->x; + q->y = -q->y; + q->z = -q->z; +} + +static float +rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) { + return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z; +} + +static void +rsQuaternionNormalize(rs_quaternion *q) { + const float len = rsQuaternionDot(q, q); + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + rsQuaternionMultiply(q, recipLen); + } +} + +static void +rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) { + if(t <= 0.0f) { + rsQuaternionSet(q, q0); + return; + } + if(t >= 1.0f) { + rsQuaternionSet(q, q1); + return; + } + + rs_quaternion tempq0, tempq1; + rsQuaternionSet(&tempq0, q0); + rsQuaternionSet(&tempq1, q1); + + float angle = rsQuaternionDot(q0, q1); + if(angle < 0) { + rsQuaternionMultiply(&tempq0, -1.0f); + angle *= -1.0f; + } + + float scale, invScale; + if (angle + 1.0f > 0.05f) { + if (1.0f - angle >= 0.05f) { + float theta = acos(angle); + float invSinTheta = 1.0f / sin(theta); + scale = sin(theta * (1.0f - t)) * invSinTheta; + invScale = sin(theta * t) * invSinTheta; + } + else { + scale = 1.0f - t; + invScale = t; + } + } + else { + rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w); + scale = sin(M_PI * (0.5f - t)); + invScale = sin(M_PI * t); + } + + rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale, + tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale); +} + +static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) { + float x2 = 2.0f * q->x * q->x; + float y2 = 2.0f * q->y * q->y; + float z2 = 2.0f * q->z * q->z; + float xy = 2.0f * q->x * q->y; + float wz = 2.0f * q->w * q->z; + float xz = 2.0f * q->x * q->z; + float wy = 2.0f * q->w * q->y; + float wx = 2.0f * q->w * q->x; + float yz = 2.0f * q->y * q->z; + + m->m[0] = 1.0f - y2 - z2; + m->m[1] = xy - wz; + m->m[2] = xz + wy; + m->m[3] = 0.0f; + + m->m[4] = xy + wz; + m->m[5] = 1.0f - x2 - z2; + m->m[6] = yz - wx; + m->m[7] = 0.0f; + + m->m[8] = xz - wy; + m->m[9] = yz - wx; + m->m[10] = 1.0f - x2 - y2; + m->m[11] = 0.0f; + + m->m[12] = 0.0f; + m->m[13] = 0.0f; + m->m[14] = 0.0f; + m->m[15] = 1.0f; +} + ///////////////////////////////////////////////////// // int ops diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index 69e1aed..ddae7eb 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -67,6 +67,10 @@ typedef struct { float m[4]; } rs_matrix2x2; +typedef struct { + float w, x, y, z; +} rs_quaternion; + #define RS_PACKED __attribute__((packed, aligned(4))) diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 60a0d82..e09e755 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -232,6 +232,12 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l } } +void AssetManager::getConfiguration(ResTable_config* outConfig) const +{ + AutoMutex _l(mLock); + *outConfig = *mConfig; +} + /* * Open an asset. * diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index fe49300..adedf0c 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -156,9 +156,9 @@ bool ObbFile::parseObbFile(int fd) return false; } - if (footerSize < kFooterMinSize) { - LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n", - footerSize, kFooterMinSize); + if (footerSize < (kFooterMinSize - kFooterTagSize)) { + LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", + footerSize, kFooterMinSize - kFooterTagSize); return false; } } @@ -169,6 +169,8 @@ bool ObbFile::parseObbFile(int fd) return false; } + mFooterStart = fileOffset; + char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); @@ -293,4 +295,38 @@ bool ObbFile::writeTo(int fd) return true; } +bool ObbFile::removeFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDWR); + if (fd < 0) { + goto out; + } + success = removeFrom(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::removeFrom(int fd) +{ + if (fd < 0) { + return false; + } + + if (!readFrom(fd)) { + return false; + } + + ftruncate(fd, mFooterStart); + + return true; +} + } diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 5e7dd5c..9ccd140 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -260,6 +260,7 @@ void CameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) { void CameraSource::signalBufferReturned(MediaBuffer *buffer) { LOGV("signalBufferReturned: %p", buffer->data()); + Mutex::Autolock autoLock(mLock); for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin(); it != mFramesBeingEncoded.end(); ++it) { if ((*it)->pointer() == buffer->data()) { @@ -327,6 +328,7 @@ status_t CameraSource::read( (*buffer)->setObserver(this); (*buffer)->add_ref(); (*buffer)->meta_data()->setInt64(kKeyTime, frameTime); + return OK; } } diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 1fd256a..ba99501 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -187,6 +187,7 @@ void CameraSourceTimeLapse::stopCameraRecording() { if (mUseStillCameraForTimeLapse) { void *dummy; pthread_join(mThreadTimeLapse, &dummy); + CHECK_EQ(OK, mCamera->startPreview()); } else { mCamera->stopRecording(); } diff --git a/native/android/Android.mk b/native/android/Android.mk index 950a1e9..bd2b27a 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -7,6 +7,7 @@ include $(CLEAR_VARS) # LOCAL_SRC_FILES:= \ asset_manager.cpp \ + configuration.cpp \ input.cpp \ looper.cpp \ native_activity.cpp \ diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 36c381e..3f7c1b6 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "NAsset" #include <utils/Log.h> -#include <android/asset_manager.h> +#include <android/asset_manager_jni.h> #include <utils/AssetManager.h> #include <utils/AssetDir.h> #include <utils/Asset.h> diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp new file mode 100644 index 0000000..d76164f --- /dev/null +++ b/native/android/configuration.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2010 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. + */ + +#define LOG_TAG "Configuration" +#include <utils/Log.h> + +#include <utils/AssetManager.h> + +#include <android_runtime/android_content_res_Configuration.h> + +using namespace android; + +AConfiguration* AConfiguration_new() { + AConfiguration* config = new AConfiguration; + memset(config, 0, sizeof(AConfiguration)); + return config; +} + +void AConfiguration_delete(AConfiguration* config) { + delete config; +} + +void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) { + ((AssetManager*)am)->getConfiguration(out); +} + +void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) { + *dest = *src; +} + +int32_t AConfiguration_getMcc(AConfiguration* config) { + return config->mcc; +} + +int32_t AConfiguration_getMnc(AConfiguration* config) { + return config->mnc; +} + +void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage) { + outLanguage[0] = config->language[0]; + outLanguage[1] = config->language[1]; +} + +void AConfiguration_getCountry(AConfiguration* config, char* outCountry) { + outCountry[0] = config->country[0]; + outCountry[1] = config->country[1]; +} + +int32_t AConfiguration_getOrientation(AConfiguration* config) { + return config->orientation; +} + +int32_t AConfiguration_getTouchscreen(AConfiguration* config) { + return config->touchscreen; +} + +int32_t AConfiguration_getDensity(AConfiguration* config) { + return config->density; +} + +int32_t AConfiguration_getKeyboard(AConfiguration* config) { + return config->keyboard; +} + +int32_t AConfiguration_getNavigation(AConfiguration* config) { + return config->navigation; +} + +int32_t AConfiguration_getKeysHidden(AConfiguration* config) { + return config->inputFlags&ResTable_config::MASK_KEYSHIDDEN; +} + +int32_t AConfiguration_getNavHidden(AConfiguration* config) { + return (config->inputFlags&ResTable_config::MASK_NAVHIDDEN) + >> ResTable_config::SHIFT_NAVHIDDEN; +} + +int32_t AConfiguration_getSdkVersion(AConfiguration* config) { + return config->sdkVersion; +} + +int32_t AConfiguration_getScreenSize(AConfiguration* config) { + return config->screenLayout&ResTable_config::MASK_SCREENSIZE; +} + +int32_t AConfiguration_getScreenLong(AConfiguration* config) { + return (config->screenLayout&ResTable_config::MASK_SCREENLONG) + >> ResTable_config::SHIFT_SCREENLONG; +} + +int32_t AConfiguration_getUiModeType(AConfiguration* config) { + return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE; +} + +int32_t AConfiguration_getUiModeNight(AConfiguration* config) { + return (config->uiMode&ResTable_config::MASK_UI_MODE_NIGHT) + >> ResTable_config::SHIFT_UI_MODE_NIGHT; + +} + +// ---------------------------------------------------------------------- + +void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) { + config->mcc = mcc; +} + +void AConfiguration_setMnc(AConfiguration* config, int32_t mnc) { + config->mnc = mnc; +} + +void AConfiguration_setLanguage(AConfiguration* config, const char* language) { + config->language[0] = language[0]; + config->language[1] = language[1]; +} + +void AConfiguration_setCountry(AConfiguration* config, const char* country) { + config->country[0] = country[0]; + config->country[1] = country[1]; +} + +void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation) { + config->orientation = orientation; +} + +void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen) { + config->touchscreen = touchscreen; +} + +void AConfiguration_setDensity(AConfiguration* config, int32_t density) { + config->density = density; +} + +void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard) { + config->keyboard = keyboard; +} + +void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation) { + config->navigation = navigation; +} + +void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden) { + config->inputFlags = (config->inputFlags&~ResTable_config::MASK_KEYSHIDDEN) + | (keysHidden&ResTable_config::MASK_KEYSHIDDEN); +} + +void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden) { + config->inputFlags = (config->inputFlags&~ResTable_config::MASK_NAVHIDDEN) + | ((navHidden<<ResTable_config::SHIFT_NAVHIDDEN)&ResTable_config::MASK_NAVHIDDEN); +} + +void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion) { + config->sdkVersion = sdkVersion; +} + +void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize) { + config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | (screenSize&ResTable_config::MASK_SCREENSIZE); +} + +void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) { + config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG); +} + +void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) { + config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | (uiModeType&ResTable_config::MASK_UI_MODE_TYPE); +} + +void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight) { + config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ((uiModeNight<<ResTable_config::SHIFT_UI_MODE_NIGHT)&ResTable_config::MASK_UI_MODE_NIGHT); + +} + +// ---------------------------------------------------------------------- + +int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) { + return (config1->diff(*config2)); +} + +int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested) { + return base->match(*requested); +} + +int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test, + AConfiguration* requested) { + return base->isBetterThan(*test, requested); +} diff --git a/native/copy-to-ndk.sh b/native/copy-to-ndk.sh new file mode 100644 index 0000000..4f5a16a --- /dev/null +++ b/native/copy-to-ndk.sh @@ -0,0 +1,55 @@ +# Take care of copying current header files over to the correct +# location in the NDK. + +copyndkheaders() { + local CURR_PLATFORM=android-9 + local ALL_PLATFORMS="$CURR_PLATFORM android-8 android-5 android-4 android-3" + + local SRC_HEADERS=$ANDROID_BUILD_TOP/frameworks/base/native/include/android + local NDK_PLATFORMS=$ANDROID_BUILD_TOP/development/ndk/platforms + local DST_HEADERS=$NDK_PLATFORMS/$CURR_PLATFORM + + local SRC_LIB_ANDROID=$ANDROID_PRODUCT_OUT/system/lib/libandroid.so + local DST_LIB_ANDROID=$NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/lib/libandroid.so + + local didsomething="" + + #echo "SRC_HEADERS: $SRC_HEADERS" + + for i in $(cd $SRC_HEADERS; ls *.h); do + local src=$SRC_HEADERS/$i + local changed="" + for j in $ALL_PLATFORMS; do + local dst=$NDK_PLATFORMS/$j/arch-arm/usr/include/android/$i + if [ "$changed" == "" -a -e $dst ]; then + #echo "Exists: $dst" + if diff $src $dst >/dev/null; then + echo "$i: has not changed from $j" >/dev/null + changed="false" + else + changed="true" + echo "$i: has changed from $j" >/dev/null + fi + fi + done + if [ "$changed" == "true" -o "$changed" == "" ]; then + echo "Updating: $i" + cp $src $NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/include/android/$i + didsomething="true" + fi + done + + if diff $SRC_LIB_ANDROID $DST_LIB_ANDROID >/dev/null; then + echo "libandroid.so: has not changed" >/dev/null + else + echo "Updating: $DST_LIB_ANDROID" + cp $SRC_LIB_ANDROID $DST_LIB_ANDROID + didsomething="true" + fi + if [ "$didsomething" != "" ]; then + echo "Headers changed... rebuilding platforms." + sh $ANDROID_BUILD_TOP/ndk/build/tools/build-platforms.sh + fi +} + +copyndkheaders diff --git a/native/include/android/asset_manager.h b/native/include/android/asset_manager.h index 89989f8..4fa0ef3 100644 --- a/native/include/android/asset_manager.h +++ b/native/include/android/asset_manager.h @@ -18,8 +18,6 @@ #ifndef ANDROID_ASSET_MANAGER_H #define ANDROID_ASSET_MANAGER_H -#include <jni.h> - #ifdef __cplusplus extern "C" { #endif @@ -43,14 +41,6 @@ enum { /** - * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager - * object. Note that the caller is responsible for obtaining and holding a VM reference - * to the jobject to prevent its being garbage collected while the native object is - * in use. - */ -AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); - -/** * Open the named directory within the asset hierarchy. The directory can then * be inspected with the AAssetDir functions. To open the top-level directory, * pass in "" as the dirName. diff --git a/native/include/android/asset_manager_jni.h b/native/include/android/asset_manager_jni.h new file mode 100644 index 0000000..aec2d3c --- /dev/null +++ b/native/include/android/asset_manager_jni.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 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. + */ + + +#ifndef ANDROID_ASSET_MANAGER_JNI_H +#define ANDROID_ASSET_MANAGER_JNI_H + +#include <android/asset_manager.h> +#include <jni.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager + * object. Note that the caller is responsible for obtaining and holding a VM reference + * to the jobject to prevent its being garbage collected while the native object is + * in use. + */ +AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_ASSET_MANAGER_JNI_H diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h new file mode 100644 index 0000000..79b9b1e --- /dev/null +++ b/native/include/android/configuration.h @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef ANDROID_CONFIGURATION_H +#define ANDROID_CONFIGURATION_H + +#include <android/asset_manager.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct AConfiguration; +typedef struct AConfiguration AConfiguration; + +enum { + ACONFIGURATION_ORIENTATION_ANY = 0x0000, + ACONFIGURATION_ORIENTATION_PORT = 0x0001, + ACONFIGURATION_ORIENTATION_LAND = 0x0002, + ACONFIGURATION_ORIENTATION_SQUARE = 0x0003, + + ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000, + ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001, + ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002, + ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003, + + ACONFIGURATION_DENSITY_DEFAULT = 0, + ACONFIGURATION_DENSITY_LOW = 120, + ACONFIGURATION_DENSITY_MEDIUM = 160, + ACONFIGURATION_DENSITY_HIGH = 240, + ACONFIGURATION_DENSITY_NONE = 0xffff, + + ACONFIGURATION_KEYBOARD_ANY = 0x0000, + ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001, + ACONFIGURATION_KEYBOARD_QWERTY = 0x0002, + ACONFIGURATION_KEYBOARD_12KEY = 0x0003, + + ACONFIGURATION_NAVIGATION_ANY = 0x0000, + ACONFIGURATION_NAVIGATION_NONAV = 0x0001, + ACONFIGURATION_NAVIGATION_DPAD = 0x0002, + ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003, + ACONFIGURATION_NAVIGATION_WHEEL = 0x0004, + + ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000, + ACONFIGURATION_KEYSHIDDEN_NO = 0x0001, + ACONFIGURATION_KEYSHIDDEN_YES = 0x0002, + ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003, + + ACONFIGURATION_NAVHIDDEN_ANY = 0x0000, + ACONFIGURATION_NAVHIDDEN_NO = 0x0001, + ACONFIGURATION_NAVHIDDEN_YES = 0x0002, + + ACONFIGURATION_SCREENSIZE_ANY = 0x00, + ACONFIGURATION_SCREENSIZE_SMALL = 0x01, + ACONFIGURATION_SCREENSIZE_NORMAL = 0x02, + ACONFIGURATION_SCREENSIZE_LARGE = 0x03, + ACONFIGURATION_SCREENSIZE_XLARGE = 0x04, + + ACONFIGURATION_SCREENLONG_ANY = 0x00, + ACONFIGURATION_SCREENLONG_NO = 0x1, + ACONFIGURATION_SCREENLONG_YES = 0x2, + + ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00, + ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, + ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, + ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, + + ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, + ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10, + ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20, + + ACONFIGURATION_MCC = 0x0001, + ACONFIGURATION_MNC = 0x0002, + ACONFIGURATION_LOCALE = 0x0004, + ACONFIGURATION_TOUCHSCREEN = 0x0008, + ACONFIGURATION_KEYBOARD = 0x0010, + ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020, + ACONFIGURATION_NAVIGATION = 0x0040, + ACONFIGURATION_ORIENTATION = 0x0080, + ACONFIGURATION_DENSITY = 0x0100, + ACONFIGURATION_SCREEN_SIZE = 0x0200, + ACONFIGURATION_VERSION = 0x0400, + ACONFIGURATION_SCREEN_LAYOUT = 0x0800, + ACONFIGURATION_UI_MODE = 0x1000, +}; + +/** + * Create a new AConfiguration, initialized with no values set. + */ +AConfiguration* AConfiguration_new(); + +/** + * Free an AConfiguration that was previously created with + * AConfiguration_new(). + */ +void AConfiguration_delete(AConfiguration* config); + +/** + * Create and return a new AConfiguration based on the current configuration in + * use in the given AssetManager. + */ +void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am); + +/** + * Copy the contents of 'src' to 'dest'. + */ +void AConfiguration_copy(AConfiguration* dest, AConfiguration* src); + +/** + * Return the current MCC set in the configuration. 0 if not set. + */ +int32_t AConfiguration_getMcc(AConfiguration* config); + +/** + * Set the current MCC in the configuration. 0 to clear. + */ +void AConfiguration_setMcc(AConfiguration* config, int32_t mcc); + +/** + * Return the current MNC set in the configuration. 0 if not set. + */ +int32_t AConfiguration_getMnc(AConfiguration* config); + +/** + * Set the current MNC in the configuration. 0 to clear. + */ +void AConfiguration_setMnc(AConfiguration* config, int32_t mnc); + +/** + * Return the current language code set in the configuration. The output will + * be filled with an array of two characters. They are not 0-terminated. If + * a language is not set, they will be 0. + */ +void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage); + +/** + * Set the current language code in the configuration, from the first two + * characters in the string. + */ +void AConfiguration_setLanguage(AConfiguration* config, const char* language); + +/** + * Return the current country code set in the configuration. The output will + * be filled with an array of two characters. They are not 0-terminated. If + * a country is not set, they will be 0. + */ +void AConfiguration_getCountry(AConfiguration* config, char* outCountry); + +/** + * Set the current country code in the configuration, from the first two + * characters in the string. + */ +void AConfiguration_setCountry(AConfiguration* config, const char* country); + +/** + * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration. + */ +int32_t AConfiguration_getOrientation(AConfiguration* config); + +/** + * Set the current orientation in the configuration. + */ +void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation); + +/** + * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration. + */ +int32_t AConfiguration_getTouchscreen(AConfiguration* config); + +/** + * Set the current touchscreen in the configuration. + */ +void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen); + +/** + * Return the current ACONFIGURATION_DENSITY_* set in the configuration. + */ +int32_t AConfiguration_getDensity(AConfiguration* config); + +/** + * Set the current density in the configuration. + */ +void AConfiguration_setDensity(AConfiguration* config, int32_t density); + +/** + * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration. + */ +int32_t AConfiguration_getKeyboard(AConfiguration* config); + +/** + * Set the current keyboard in the configuration. + */ +void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard); + +/** + * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration. + */ +int32_t AConfiguration_getNavigation(AConfiguration* config); + +/** + * Set the current navigation in the configuration. + */ +void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation); + +/** + * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration. + */ +int32_t AConfiguration_getKeysHidden(AConfiguration* config); + +/** + * Set the current keys hidden in the configuration. + */ +void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden); + +/** + * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration. + */ +int32_t AConfiguration_getNavHidden(AConfiguration* config); + +/** + * Set the current nav hidden in the configuration. + */ +void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden); + +/** + * Return the current SDK (API) version set in the configuration. + */ +int32_t AConfiguration_getSdkVersion(AConfiguration* config); + +/** + * Set the current SDK version in the configuration. + */ +void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion); + +/** + * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration. + */ +int32_t AConfiguration_getScreenSize(AConfiguration* config); + +/** + * Set the current screen size in the configuration. + */ +void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize); + +/** + * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration. + */ +int32_t AConfiguration_getScreenLong(AConfiguration* config); + +/** + * Set the current screen long in the configuration. + */ +void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong); + +/** + * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration. + */ +int32_t AConfiguration_getUiModeType(AConfiguration* config); + +/** + * Set the current UI mode type in the configuration. + */ +void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType); + +/** + * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration. + */ +int32_t AConfiguration_getUiModeNight(AConfiguration* config); + +/** + * Set the current UI mode night in the configuration. + */ +void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight); + +/** + * Perform a diff between two configurations. Returns a bit mask of + * ACONFIGURATION_* constants, each bit set meaning that configuration element + * is different between them. + */ +int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2); + +/** + * Determine whether 'base' is a valid configuration for use within the + * environment 'requested'. Returns 0 if there are any values in 'base' + * that conflict with 'requested'. Returns 1 if it does not conflict. + */ +int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested); + +/** + * Determine whether the configuration in 'test' is better than the existing + * configuration in 'base'. If 'requested' is non-NULL, this decision is based + * on the overall configuration given there. If it is NULL, this decision is + * simply based on which configuration is more specific. Returns non-0 if + * 'test' is better than 'base'. + * + * This assumes you have already filtered the configurations with + * AConfiguration_match(). + */ +int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test, + AConfiguration* requested); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_CONFIGURATION_H diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h index ee4204d..d74e1ce 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -197,6 +197,12 @@ typedef struct ANativeActivityCallbacks { void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect); /** + * The current device AConfiguration has changed. The new configuration can + * be retrieved from assetManager. + */ + void (*onConfigurationChanged)(ANativeActivity* activity); + + /** * The system is running low on memory. Use this callback to release * resources you do not need, to help the system avoid killing more * important processes. @@ -208,7 +214,9 @@ typedef struct ANativeActivityCallbacks { * This is the function that must be in the native code to instantiate the * application's native activity. It is called with the activity instance (see * above); if the code is being instantiated from a previously saved instance, - * the savedState will be non-NULL and point to the saved data. + * the savedState will be non-NULL and point to the saved data. You must make + * any copy of this data you need -- it will be released after you return from + * this function. */ typedef void ANativeActivity_createFunc(ANativeActivity* activity, void* savedState, size_t savedStateSize); diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h index 7599d7e..ad03d0e 100644 --- a/native/include/android/native_window.h +++ b/native/include/android/native_window.h @@ -36,12 +36,23 @@ struct ANativeWindow; typedef struct ANativeWindow ANativeWindow; typedef struct ANativeWindow_Buffer { + // The number of pixels that are show horizontally. int32_t width; + + // The number of pixels that are shown vertically. int32_t height; + + // The number of *pixels* that a line in the buffer takes in + // memory. This may be >= width. int32_t stride; + + // The format of the buffer. One of WINDOW_FORMAT_* int32_t format; + + // The actual bits. void* bits; + // Do not touch. uint32_t reserved[6]; } ANativeWindow_Buffer; diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index f1c6532..c6e0a24 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -24,6 +24,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.res.ObbInfo; +import android.content.res.ObbScanner; import android.net.Uri; import android.os.Environment; import android.os.IBinder; @@ -142,6 +144,10 @@ public class DefaultContainerService extends IntentService { public boolean checkFreeStorage(boolean external, Uri fileUri) { return checkFreeStorageInner(external, fileUri); } + + public ObbInfo getObbInfo(String filename) { + return ObbScanner.getObbInfo(filename); + } }; public DefaultContainerService() { diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml index dc2357f..c32e997 100644 --- a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml +++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml @@ -27,9 +27,10 @@ <com.android.systemui.statusbar.Clock style="@*android:style/TextAppearance.StatusBar.Icon" android:id="@+id/clock" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" + android:gravity="center" android:textSize="16sp" android:textStyle="bold" android:padding="2dip" diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index cc1566b..ae4e168 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -314,14 +314,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { case ConnectivityManager.TYPE_WIFI: if (DBG) Slog.v(TAG, "Starting Wifi Service."); WifiStateTracker wst = new WifiStateTracker(context, mHandler); - WifiService wifiService = new WifiService(context, wst); + WifiService wifiService = new WifiService(context); ServiceManager.addService(Context.WIFI_SERVICE, wifiService); - wifiService.startWifi(); + wifiService.checkAndStartWifi(); mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; wst.startMonitoring(); //TODO: as part of WWS refactor, create only when needed - mWifiWatchdogService = new WifiWatchdogService(context, wst); + mWifiWatchdogService = new WifiWatchdogService(context); break; case ConnectivityManager.TYPE_MOBILE: @@ -1205,16 +1205,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { sendConnectedBroadcast(info); } - private void handleScanResultsAvailable(NetworkInfo info) { - int networkType = info.getType(); - if (networkType != ConnectivityManager.TYPE_WIFI) { - if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " + - info.getTypeName() + " network. Don't know how to handle."); - } - - mNetTrackers[networkType].interpretScanResultsAvailable(); - } - private void handleNotificationChange(boolean visible, int id, Notification notification) { NotificationManager notificationManager = (NotificationManager) mContext @@ -1619,11 +1609,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } break; - case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE: - info = (NetworkInfo) msg.obj; - handleScanResultsAvailable(info); - break; - case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED: handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 3fd9907..164142e 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -466,6 +466,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); + mStatusBar = statusBar; + statusBar.setIconVisibility("ime", false); + buildInputMethodListLocked(mMethodList, mMethodMap); final String enabledStr = Settings.Secure.getString( @@ -509,9 +512,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - mStatusBar = statusBar; - statusBar.setIconVisibility("ime", false); - mSettingsObserver = new SettingsObserver(mHandler); updateFromSettingsLocked(); } @@ -1220,6 +1220,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurClient == null || client == null || mCurClient.client.asBinder() != client.asBinder()) { Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client); + return; } mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index ef5e9cc..d604886 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -16,34 +16,42 @@ package com.android.server; +import com.android.internal.app.IMediaContainerService; import com.android.server.am.ActivityManagerService; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.res.ObbInfo; -import android.content.res.ObbScanner; import android.net.Uri; -import android.os.storage.IMountService; -import android.os.storage.IMountServiceListener; -import android.os.storage.IMountShutdownObserver; -import android.os.storage.StorageResultCode; import android.os.Binder; +import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.os.IBinder; -import android.os.Environment; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.os.storage.IMountShutdownObserver; +import android.os.storage.IObbActionListener; +import android.os.storage.StorageResultCode; import android.util.Slog; + import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; /** * MountService implements back-end services for platform storage @@ -137,9 +145,77 @@ class MountService extends IMountService.Stub final private HashSet<String> mAsecMountSet = new HashSet<String>(); /** - * Private hash of currently mounted filesystem images. + * Mounted OBB tracking information. Used to track the current state of all + * OBBs. + */ + final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>(); + final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>(); + + class ObbState implements IBinder.DeathRecipient { + public ObbState(String filename, IObbActionListener token, int callerUid) { + this.filename = filename; + this.token = token; + this.callerUid = callerUid; + mounted = false; + } + + // OBB source filename + String filename; + + // Token of remote Binder caller + IObbActionListener token; + + // Binder.callingUid() + public int callerUid; + + // Whether this is mounted currently. + boolean mounted; + + @Override + public void binderDied() { + ObbAction action = new UnmountObbAction(this, true); + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); + + removeObbState(this); + + token.asBinder().unlinkToDeath(this, 0); + } + } + + // OBB Action Handler + final private ObbActionHandler mObbActionHandler; + + // OBB action handler messages + private static final int OBB_RUN_ACTION = 1; + private static final int OBB_MCS_BOUND = 2; + private static final int OBB_MCS_UNBIND = 3; + private static final int OBB_MCS_RECONNECT = 4; + private static final int OBB_MCS_GIVE_UP = 5; + + /* + * Default Container Service information */ - final private HashSet<String> mObbMountSet = new HashSet<String>(); + static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( + "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService"); + + final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); + + class DefaultContainerConnection implements ServiceConnection { + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG_OBB) + Slog.i(TAG, "onServiceConnected"); + IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs)); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG_OBB) + Slog.i(TAG, "onServiceDisconnected"); + } + }; + + // Used in the ObbActionHandler + private IMediaContainerService mContainerService = null; // Handler messages private static final int H_UNMOUNT_PM_UPDATE = 1; @@ -363,7 +439,7 @@ class MountService extends IMountService.Stub public void binderDied() { if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!"); - synchronized(mListeners) { + synchronized (mListeners) { mListeners.remove(this); mListener.asBinder().unlinkToDeath(this, 0); } @@ -917,6 +993,9 @@ class MountService extends IMountService.Stub mHandlerThread.start(); mHandler = new MountServiceHandler(mHandlerThread.getLooper()); + // Add OBB Action Handler to MountService thread. + mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper()); + /* * Vold does not run in the simulator, so pretend the connector thread * ran and did its thing. @@ -1013,7 +1092,7 @@ class MountService extends IMountService.Stub private void setUmsEnabling(boolean enable) { synchronized (mListeners) { - mUmsEnabling = true; + mUmsEnabling = enable; } } @@ -1351,12 +1430,16 @@ class MountService extends IMountService.Stub mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); } - private boolean isCallerOwnerOfPackage(String packageName) { + private boolean isCallerOwnerOfPackageOrSystem(String packageName) { final int callerUid = Binder.getCallingUid(); - return isUidOwnerOfPackage(packageName, callerUid); + return isUidOwnerOfPackageOrSystem(packageName, callerUid); } - private boolean isUidOwnerOfPackage(String packageName, int callerUid) { + private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { + if (callerUid == android.os.Process.SYSTEM_UID) { + return true; + } + if (packageName == null) { return false; } @@ -1375,12 +1458,6 @@ class MountService extends IMountService.Stub waitForReady(); warnOnNotMounted(); - // XXX replace with call to IMediaContainerService - ObbInfo obbInfo = ObbScanner.getObbInfo(filename); - if (!isCallerOwnerOfPackage(obbInfo.packageName)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); - } - try { ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename)); String []tok = rsp.get(0).split(" "); @@ -1392,7 +1469,7 @@ class MountService extends IMountService.Stub } catch (NativeDaemonConnectorException e) { int code = e.getCode(); if (code == VoldResponseCode.OpFailedStorageNotFound) { - throw new IllegalArgumentException(String.format("OBB '%s' not found", filename)); + return null; } else { throw new IllegalStateException(String.format("Unexpected response code %d", code)); } @@ -1400,95 +1477,390 @@ class MountService extends IMountService.Stub } public boolean isObbMounted(String filename) { + synchronized (mObbMounts) { + return mObbPathToStateMap.containsKey(filename); + } + } + + public void mountObb(String filename, String key, IObbActionListener token) { waitForReady(); warnOnNotMounted(); - // XXX replace with call to IMediaContainerService - ObbInfo obbInfo = ObbScanner.getObbInfo(filename); - if (!isCallerOwnerOfPackage(obbInfo.packageName)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); + final ObbState obbState; + + synchronized (mObbMounts) { + if (isObbMounted(filename)) { + throw new IllegalArgumentException("OBB file is already mounted"); + } + + if (mObbMounts.containsKey(token)) { + throw new IllegalArgumentException("You may only have one OBB mounted at a time"); + } + + final int callerUid = Binder.getCallingUid(); + obbState = new ObbState(filename, token, callerUid); + addObbState(obbState); } - synchronized (mObbMountSet) { - return mObbMountSet.contains(filename); + try { + token.asBinder().linkToDeath(obbState, 0); + } catch (RemoteException rex) { + Slog.e(TAG, "Failed to link to listener death"); } + + MountObbAction action = new MountObbAction(obbState, key); + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); + + if (DEBUG_OBB) + Slog.i(TAG, "Send to OBB handler: " + action.toString()); } - public int mountObb(String filename, String key) { - waitForReady(); - warnOnNotMounted(); + public void unmountObb(String filename, boolean force, IObbActionListener token) { + final ObbState obbState; - synchronized (mObbMountSet) { - if (mObbMountSet.contains(filename)) { - return StorageResultCode.OperationFailedStorageMounted; + synchronized (mObbMounts) { + if (!isObbMounted(filename)) { + throw new IllegalArgumentException("OBB is not mounted"); } + obbState = mObbPathToStateMap.get(filename); } - final int callerUid = Binder.getCallingUid(); + UnmountObbAction action = new UnmountObbAction(obbState, force); + mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); + + if (DEBUG_OBB) + Slog.i(TAG, "Send to OBB handler: " + action.toString()); + } - // XXX replace with call to IMediaContainerService - ObbInfo obbInfo = ObbScanner.getObbInfo(filename); - if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); + private void addObbState(ObbState obbState) { + synchronized (mObbMounts) { + mObbMounts.put(obbState.token, obbState); + mObbPathToStateMap.put(obbState.filename, obbState); } + } - if (key == null) { - key = "none"; + private void removeObbState(ObbState obbState) { + synchronized (mObbMounts) { + mObbMounts.remove(obbState.token); + mObbPathToStateMap.remove(obbState.filename); } + } - int rc = StorageResultCode.OperationSucceeded; - String cmd = String.format("obb mount %s %s %d", filename, key, callerUid); - try { - mConnector.doCommand(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code != VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedInternalError; + private class ObbActionHandler extends Handler { + private boolean mBound = false; + private List<ObbAction> mActions = new LinkedList<ObbAction>(); + + ObbActionHandler(Looper l) { + super(l); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case OBB_RUN_ACTION: { + ObbAction action = (ObbAction) msg.obj; + + if (DEBUG_OBB) + Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); + + // If a bind was already initiated we don't really + // need to do anything. The pending install + // will be processed later on. + if (!mBound) { + // If this is the only one pending we might + // have to bind to the service again. + if (!connectToService()) { + Slog.e(TAG, "Failed to bind to media container service"); + action.handleError(); + return; + } else { + // Once we bind to the service, the first + // pending request will be processed. + mActions.add(action); + } + } else { + // Already bound to the service. Just make + // sure we trigger off processing the first request. + if (mActions.size() == 0) { + mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); + } + + mActions.add(action); + } + break; + } + case OBB_MCS_BOUND: { + if (DEBUG_OBB) + Slog.i(TAG, "OBB_MCS_BOUND"); + if (msg.obj != null) { + mContainerService = (IMediaContainerService) msg.obj; + } + if (mContainerService == null) { + // Something seriously wrong. Bail out + Slog.e(TAG, "Cannot bind to media container service"); + for (ObbAction action : mActions) { + // Indicate service bind error + action.handleError(); + } + mActions.clear(); + } else if (mActions.size() > 0) { + ObbAction action = mActions.get(0); + if (action != null) { + action.execute(this); + } + } else { + // Should never happen ideally. + Slog.w(TAG, "Empty queue"); + } + break; + } + case OBB_MCS_RECONNECT: { + if (DEBUG_OBB) + Slog.i(TAG, "OBB_MCS_RECONNECT"); + if (mActions.size() > 0) { + if (mBound) { + disconnectService(); + } + if (!connectToService()) { + Slog.e(TAG, "Failed to bind to media container service"); + for (ObbAction action : mActions) { + // Indicate service bind error + action.handleError(); + } + mActions.clear(); + } + } + break; + } + case OBB_MCS_UNBIND: { + if (DEBUG_OBB) + Slog.i(TAG, "OBB_MCS_UNBIND"); + + // Delete pending install + if (mActions.size() > 0) { + mActions.remove(0); + } + if (mActions.size() == 0) { + if (mBound) { + disconnectService(); + } + } else { + // There are more pending requests in queue. + // Just post MCS_BOUND message to trigger processing + // of next pending install. + mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); + } + break; + } + case OBB_MCS_GIVE_UP: { + if (DEBUG_OBB) + Slog.i(TAG, "OBB_MCS_GIVE_UP"); + mActions.remove(0); + break; + } } } - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mObbMountSet) { - mObbMountSet.add(filename); + private boolean connectToService() { + if (DEBUG_OBB) + Slog.i(TAG, "Trying to bind to DefaultContainerService"); + + Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); + if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) { + mBound = true; + return true; } + return false; + } + + private void disconnectService() { + mContainerService = null; + mBound = false; + mContext.unbindService(mDefContainerConn); } - return rc; } - public int unmountObb(String filename, boolean force) { - waitForReady(); - warnOnNotMounted(); + abstract class ObbAction { + private static final int MAX_RETRIES = 3; + private int mRetries; + + ObbState mObbState; - ObbInfo obbInfo = ObbScanner.getObbInfo(filename); - if (!isCallerOwnerOfPackage(obbInfo.packageName)) { - throw new IllegalArgumentException("Caller package does not match OBB file"); + ObbAction(ObbState obbState) { + mObbState = obbState; } - synchronized (mObbMountSet) { - if (!mObbMountSet.contains(filename)) { - return StorageResultCode.OperationFailedStorageNotMounted; + public void execute(ObbActionHandler handler) { + try { + if (DEBUG_OBB) + Slog.i(TAG, "Starting to execute action: " + this.toString()); + mRetries++; + if (mRetries > MAX_RETRIES) { + Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); + mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP); + handleError(); + return; + } else { + handleExecute(); + if (DEBUG_OBB) + Slog.i(TAG, "Posting install MCS_UNBIND"); + mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); + } + } catch (RemoteException e) { + if (DEBUG_OBB) + Slog.i(TAG, "Posting install MCS_RECONNECT"); + mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT); + } catch (Exception e) { + if (DEBUG_OBB) + Slog.d(TAG, "Error handling OBB action", e); + handleError(); } - } + } - int rc = StorageResultCode.OperationSucceeded; - String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : "")); - try { - mConnector.doCommand(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedStorageBusy; + abstract void handleExecute() throws RemoteException; + abstract void handleError(); + } + + class MountObbAction extends ObbAction { + private String mKey; + + MountObbAction(ObbState obbState, String key) { + super(obbState); + mKey = key; + } + + public void handleExecute() throws RemoteException { + ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); + if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { + throw new IllegalArgumentException("Caller package does not match OBB file"); + } + + if (mKey == null) { + mKey = "none"; + } + + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey, + mObbState.callerUid); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code != VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedInternalError; + } + } + + if (rc == StorageResultCode.OperationSucceeded) { + try { + mObbState.token.onObbResult(mObbState.filename, "mounted"); + } catch (RemoteException e) { + Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); + } } else { - rc = StorageResultCode.OperationFailedInternalError; + Slog.e(TAG, "Couldn't mount OBB file"); + + // We didn't succeed, so remove this from the mount-set. + removeObbState(mObbState); } } - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mObbMountSet) { - mObbMountSet.remove(filename); + public void handleError() { + removeObbState(mObbState); + + try { + mObbState.token.onObbResult(mObbState.filename, "error"); + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename); } } - return rc; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("MountObbAction{"); + sb.append("filename="); + sb.append(mObbState.filename); + sb.append(",callerUid="); + sb.append(mObbState.callerUid); + sb.append(",token="); + sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL"); + sb.append('}'); + return sb.toString(); + } + } + + class UnmountObbAction extends ObbAction { + private boolean mForceUnmount; + + UnmountObbAction(ObbState obbState, boolean force) { + super(obbState); + mForceUnmount = force; + } + + public void handleExecute() throws RemoteException { + ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); + + if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) { + throw new IllegalArgumentException("Caller package does not match OBB file"); + } + + int rc = StorageResultCode.OperationSucceeded; + String cmd = String.format("obb unmount %s%s", mObbState.filename, + (mForceUnmount ? " force" : "")); + try { + mConnector.doCommand(cmd); + } catch (NativeDaemonConnectorException e) { + int code = e.getCode(); + if (code == VoldResponseCode.OpFailedStorageBusy) { + rc = StorageResultCode.OperationFailedStorageBusy; + } else { + rc = StorageResultCode.OperationFailedInternalError; + } + } + + if (rc == StorageResultCode.OperationSucceeded) { + removeObbState(mObbState); + + try { + mObbState.token.onObbResult(mObbState.filename, "unmounted"); + } catch (RemoteException e) { + Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); + } + } else { + try { + mObbState.token.onObbResult(mObbState.filename, "error"); + } catch (RemoteException e) { + Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); + } + } + } + + public void handleError() { + removeObbState(mObbState); + + try { + mObbState.token.onObbResult(mObbState.filename, "error"); + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("UnmountObbAction{"); + sb.append("filename="); + sb.append(mObbState.filename != null ? mObbState.filename : "null"); + sb.append(",force="); + sb.append(mForceUnmount); + sb.append(",callerUid="); + sb.append(mObbState.callerUid); + sb.append(",token="); + sb.append(mObbState.token != null ? mObbState.token.toString() : "null"); + sb.append('}'); + return sb.toString(); + } } } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 15080b2..b43b33e 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -17,6 +17,8 @@ package com.android.server; import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; @@ -26,25 +28,30 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.wifi.IWifiManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wifi.WifiStateTracker; +import android.net.wifi.WifiStateMachine; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; -import android.net.NetworkStateTracker; import android.net.DhcpInfo; +import android.net.NetworkInfo; +import android.net.NetworkInfo.State; import android.os.Binder; +import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; +import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; +import android.text.TextUtils; import android.util.Slog; import java.util.ArrayList; @@ -72,7 +79,7 @@ public class WifiService extends IWifiManager.Stub { private static final String TAG = "WifiService"; private static final boolean DBG = true; - private final WifiStateTracker mWifiStateTracker; + private final WifiStateMachine mWifiStateMachine; private Context mContext; @@ -123,10 +130,63 @@ public class WifiService extends IWifiManager.Stub { private boolean mIsReceiverRegistered = false; - WifiService(Context context, WifiStateTracker tracker) { + + NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); + + // Variables relating to the 'available networks' notification + /** + * The icon to show in the 'available networks' notification. This will also + * be the ID of the Notification given to the NotificationManager. + */ + private static final int ICON_NETWORKS_AVAILABLE = + com.android.internal.R.drawable.stat_notify_wifi_in_range; + /** + * When a notification is shown, we wait this amount before possibly showing it again. + */ + private final long NOTIFICATION_REPEAT_DELAY_MS; + /** + * Whether the user has set the setting to show the 'available networks' notification. + */ + private boolean mNotificationEnabled; + /** + * Observes the user setting to keep {@link #mNotificationEnabled} in sync. + */ + private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; + /** + * The {@link System#currentTimeMillis()} must be at least this value for us + * to show the notification again. + */ + private long mNotificationRepeatTime; + /** + * The Notification object given to the NotificationManager. + */ + private Notification mNotification; + /** + * Whether the notification is being shown, as set by us. That is, if the + * user cancels the notification, we will not receive the callback so this + * will still be true. We only guarantee if this is false, then the + * notification is not showing. + */ + private boolean mNotificationShown; + /** + * The number of continuous scans that must occur before consider the + * supplicant in a scanning state. This allows supplicant to associate with + * remembered networks that are in the scan results. + */ + private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; + /** + * The number of scans since the last network state change. When this + * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the + * supplicant to actually be scanning. When the network state changes to + * something other than scanning, we reset this to 0. + */ + private int mNumScansSinceNetworkStateChange; + + + WifiService(Context context) { mContext = context; - mWifiStateTracker = tracker; - mWifiStateTracker.enableRssiPolling(true); + mWifiStateMachine = new WifiStateMachine(mContext); + mWifiStateMachine.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); @@ -164,6 +224,42 @@ public class WifiService extends IWifiManager.Stub { } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); + + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + // reset & clear notification on any wifi state change + resetNotification(); + } else if (intent.getAction().equals( + WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + // reset & clear notification on a network connect & disconnect + switch(mNetworkInfo.getDetailedState()) { + case CONNECTED: + case DISCONNECTED: + resetNotification(); + break; + } + } else if (intent.getAction().equals( + WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + checkAndSetNotification(); + } + } + }, filter); + + // Setting is in seconds + NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; + mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); + mNotificationEnabledSettingObserver.register(); } /** @@ -172,7 +268,7 @@ public class WifiService extends IWifiManager.Stub { * * This function is used only at boot time */ - public void startWifi() { + public void checkAndStartWifi() { /* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */ boolean wifiEnabled = !isAirplaneModeOn() && (getPersistedWifiEnabled() || testAndClearWifiSavedState()); @@ -255,17 +351,13 @@ public class WifiService extends IWifiManager.Stub { Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0); } - NetworkStateTracker getNetworkStateTracker() { - return mWifiStateTracker; - } - /** * see {@link android.net.wifi.WifiManager#pingSupplicant()} * @return {@code true} if the operation succeeds, {@code false} otherwise */ public boolean pingSupplicant() { enforceAccessPermission(); - return mWifiStateTracker.pingSupplicant(); + return mWifiStateMachine.pingSupplicant(); } /** @@ -274,7 +366,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean startScan(boolean forceActive) { enforceChangePermission(); - return mWifiStateTracker.startScan(forceActive); + return mWifiStateMachine.startScan(forceActive); } private void enforceAccessPermission() { @@ -304,7 +396,7 @@ public class WifiService extends IWifiManager.Stub { enforceChangePermission(); if (DBG) { - Slog.e(TAG, "Invoking mWifiStateTracker.setWifiEnabled\n"); + Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); } // set a flag if the user is enabling Wifi while in airplane mode @@ -312,7 +404,7 @@ public class WifiService extends IWifiManager.Stub { mAirplaneModeOverwridden.set(true); } - mWifiStateTracker.setWifiEnabled(enable); + mWifiStateMachine.setWifiEnabled(enable); persistWifiEnabled(enable); if (enable) { @@ -338,7 +430,7 @@ public class WifiService extends IWifiManager.Stub { */ public int getWifiEnabledState() { enforceAccessPermission(); - return mWifiStateTracker.getWifiState(); + return mWifiStateMachine.getWifiState(); } /** @@ -362,7 +454,7 @@ public class WifiService extends IWifiManager.Stub { setWifiApConfiguration(wifiConfig); } - mWifiStateTracker.setWifiApEnabled(wifiConfig, enabled); + mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled); return true; } @@ -377,7 +469,7 @@ public class WifiService extends IWifiManager.Stub { */ public int getWifiApEnabledState() { enforceAccessPermission(); - return mWifiStateTracker.getWifiApState(); + return mWifiStateMachine.getWifiApState(); } /** @@ -427,7 +519,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disconnect() { enforceChangePermission(); - return mWifiStateTracker.disconnectCommand(); + return mWifiStateMachine.disconnectCommand(); } /** @@ -436,7 +528,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reconnect() { enforceChangePermission(); - return mWifiStateTracker.reconnectCommand(); + return mWifiStateMachine.reconnectCommand(); } /** @@ -445,7 +537,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reassociate() { enforceChangePermission(); - return mWifiStateTracker.reassociateCommand(); + return mWifiStateMachine.reassociateCommand(); } /** @@ -454,7 +546,7 @@ public class WifiService extends IWifiManager.Stub { */ public List<WifiConfiguration> getConfiguredNetworks() { enforceAccessPermission(); - return mWifiStateTracker.getConfiguredNetworks(); + return mWifiStateMachine.getConfiguredNetworks(); } /** @@ -464,7 +556,7 @@ public class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - return mWifiStateTracker.addOrUpdateNetwork(config); + return mWifiStateMachine.addOrUpdateNetwork(config); } /** @@ -475,7 +567,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean removeNetwork(int netId) { enforceChangePermission(); - return mWifiStateTracker.removeNetwork(netId); + return mWifiStateMachine.removeNetwork(netId); } /** @@ -487,7 +579,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - return mWifiStateTracker.enableNetwork(netId, disableOthers); + return mWifiStateMachine.enableNetwork(netId, disableOthers); } /** @@ -498,7 +590,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disableNetwork(int netId) { enforceChangePermission(); - return mWifiStateTracker.disableNetwork(netId); + return mWifiStateMachine.disableNetwork(netId); } /** @@ -511,7 +603,7 @@ public class WifiService extends IWifiManager.Stub { * Make sure we have the latest information, by sending * a status request to the supplicant. */ - return mWifiStateTracker.requestConnectionInfo(); + return mWifiStateMachine.requestConnectionInfo(); } /** @@ -521,7 +613,7 @@ public class WifiService extends IWifiManager.Stub { */ public List<ScanResult> getScanResults() { enforceAccessPermission(); - return mWifiStateTracker.getScanResultsList(); + return mWifiStateMachine.getScanResultsList(); } /** @@ -533,7 +625,7 @@ public class WifiService extends IWifiManager.Stub { public boolean saveConfiguration() { boolean result = true; enforceChangePermission(); - return mWifiStateTracker.saveConfig(); + return mWifiStateMachine.saveConfig(); } /** @@ -576,7 +668,7 @@ public class WifiService extends IWifiManager.Stub { numChannels); } - mWifiStateTracker.setNumAllowedChannels(numChannels); + mWifiStateMachine.setNumAllowedChannels(numChannels); return true; } @@ -596,7 +688,7 @@ public class WifiService extends IWifiManager.Stub { * Wi-Fi is not currently enabled), get the value from * Settings. */ - numChannels = mWifiStateTracker.getNumAllowedChannels(); + numChannels = mWifiStateMachine.getNumAllowedChannels(); if (numChannels < 0) { numChannels = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, @@ -622,9 +714,59 @@ public class WifiService extends IWifiManager.Stub { */ public DhcpInfo getDhcpInfo() { enforceAccessPermission(); - return mWifiStateTracker.getDhcpInfo(); + return mWifiStateMachine.getDhcpInfo(); + } + + /** + * see {@link android.net.wifi.WifiManager#startWifi} + * + */ + public void startWifi() { + enforceChangePermission(); + /* TODO: may be add permissions for access only to connectivity service + * TODO: if a start issued, keep wifi alive until a stop issued irrespective + * of WifiLock & device idle status unless wifi enabled status is toggled + */ + + mWifiStateMachine.setDriverStart(true); + mWifiStateMachine.reconnectCommand(); + } + + /** + * see {@link android.net.wifi.WifiManager#stopWifi} + * + */ + public void stopWifi() { + enforceChangePermission(); + /* TODO: may be add permissions for access only to connectivity service + * TODO: if a stop is issued, wifi is brought up only by startWifi + * unless wifi enabled status is toggled + */ + mWifiStateMachine.setDriverStart(false); + } + + + /** + * see {@link android.net.wifi.WifiManager#addToBlacklist} + * + */ + public void addToBlacklist(String bssid) { + enforceChangePermission(); + + mWifiStateMachine.addToBlacklist(bssid); + } + + /** + * see {@link android.net.wifi.WifiManager#clearBlacklist} + * + */ + public void clearBlacklist() { + enforceChangePermission(); + + mWifiStateMachine.clearBlacklist(); } + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -641,11 +783,11 @@ public class WifiService extends IWifiManager.Stub { mAlarmManager.cancel(mIdleIntent); mDeviceIdle = false; mScreenOff = false; - mWifiStateTracker.enableRssiPolling(true); + mWifiStateMachine.enableRssiPolling(true); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { Slog.d(TAG, "ACTION_SCREEN_OFF"); mScreenOff = true; - mWifiStateTracker.enableRssiPolling(false); + mWifiStateMachine.enableRssiPolling(false); /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off * AND the "stay on while plugged in" setting doesn't match the @@ -653,7 +795,7 @@ public class WifiService extends IWifiManager.Stub { * or plugged in to AC). */ if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) { - WifiInfo info = mWifiStateTracker.requestConnectionInfo(); + WifiInfo info = mWifiStateMachine.requestConnectionInfo(); if (info.getSupplicantState() != SupplicantState.COMPLETED) { // we used to go to sleep immediately, but this caused some race conditions // we don't have time to track down for this release. Delay instead, @@ -704,7 +846,7 @@ public class WifiService extends IWifiManager.Stub { isBluetoothPlaying = true; } } - mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying); + mWifiStateMachine.setBluetoothScanMode(isBluetoothPlaying); } else { return; @@ -771,21 +913,21 @@ public class WifiService extends IWifiManager.Stub { /* Disable tethering when airplane mode is enabled */ if (airplaneMode) { - mWifiStateTracker.setWifiApEnabled(null, false); + mWifiStateMachine.setWifiApEnabled(null, false); } if (wifiShouldBeEnabled) { if (wifiShouldBeStarted) { - mWifiStateTracker.setWifiEnabled(true); - mWifiStateTracker.setScanOnlyMode( + mWifiStateMachine.setWifiEnabled(true); + mWifiStateMachine.setScanOnlyMode( strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); - mWifiStateTracker.startWifi(true); + mWifiStateMachine.setDriverStart(true); } else { - mWifiStateTracker.requestCmWakeLock(); - mWifiStateTracker.disconnectAndStop(); + mWifiStateMachine.requestCmWakeLock(); + mWifiStateMachine.setDriverStart(false); } } else { - mWifiStateTracker.setWifiEnabled(false); + mWifiStateMachine.setWifiEnabled(false); } } @@ -832,17 +974,17 @@ public class WifiService extends IWifiManager.Stub { + ", uid=" + Binder.getCallingUid()); return; } - pw.println("Wi-Fi is " + mWifiStateTracker.getWifiStateByName()); + pw.println("Wi-Fi is " + mWifiStateMachine.getWifiStateByName()); pw.println("Stay-awake conditions: " + Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println(); pw.println("Internal state:"); - pw.println(mWifiStateTracker); + pw.println(mWifiStateMachine); pw.println(); pw.println("Latest scan results:"); - List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList(); + List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList(); if (scanResults != null && scanResults.size() != 0) { pw.println(" BSSID Frequency RSSI Flags SSID"); for (ScanResult r : scanResults) { @@ -1069,7 +1211,7 @@ public class WifiService extends IWifiManager.Stub { if (mMulticasters.size() != 0) { return; } else { - mWifiStateTracker.startPacketFiltering(); + mWifiStateMachine.startPacketFiltering(); } } } @@ -1084,7 +1226,7 @@ public class WifiService extends IWifiManager.Stub { // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each // time we're less fragile and self-healing. - mWifiStateTracker.stopPacketFiltering(); + mWifiStateMachine.stopPacketFiltering(); } int uid = Binder.getCallingUid(); @@ -1121,7 +1263,7 @@ public class WifiService extends IWifiManager.Stub { removed.unlinkDeathRecipient(); } if (mMulticasters.size() == 0) { - mWifiStateTracker.startPacketFiltering(); + mWifiStateMachine.startPacketFiltering(); } Long ident = Binder.clearCallingIdentity(); @@ -1140,4 +1282,166 @@ public class WifiService extends IWifiManager.Stub { return (mMulticasters.size() > 0); } } + + private void checkAndSetNotification() { + // If we shouldn't place a notification on available networks, then + // don't bother doing any of the following + if (!mNotificationEnabled) return; + + State state = mNetworkInfo.getState(); + if ((state == NetworkInfo.State.DISCONNECTED) + || (state == NetworkInfo.State.UNKNOWN)) { + // Look for an open network + List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList(); + if (scanResults != null) { + int numOpenNetworks = 0; + for (int i = scanResults.size() - 1; i >= 0; i--) { + ScanResult scanResult = scanResults.get(i); + + if (TextUtils.isEmpty(scanResult.capabilities)) { + numOpenNetworks++; + } + } + + if (numOpenNetworks > 0) { + if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { + /* + * We've scanned continuously at least + * NUM_SCANS_BEFORE_NOTIFICATION times. The user + * probably does not have a remembered network in range, + * since otherwise supplicant would have tried to + * associate and thus resetting this counter. + */ + setNotificationVisible(true, numOpenNetworks, false, 0); + } + return; + } + } + } + + // No open networks in range, remove the notification + setNotificationVisible(false, 0, false, 0); + } + + /** + * Clears variables related to tracking whether a notification has been + * shown recently and clears the current notification. + */ + private void resetNotification() { + mNotificationRepeatTime = 0; + mNumScansSinceNetworkStateChange = 0; + setNotificationVisible(false, 0, false, 0); + } + + /** + * Display or don't display a notification that there are open Wi-Fi networks. + * @param visible {@code true} if notification should be visible, {@code false} otherwise + * @param numNetworks the number networks seen + * @param force {@code true} to force notification to be shown/not-shown, + * even if it is already shown/not-shown. + * @param delay time in milliseconds after which the notification should be made + * visible or invisible. + */ + private void setNotificationVisible(boolean visible, int numNetworks, boolean force, + int delay) { + + // Since we use auto cancel on the notification, when the + // mNetworksAvailableNotificationShown is true, the notification may + // have actually been canceled. However, when it is false we know + // for sure that it is not being shown (it will not be shown any other + // place than here) + + // If it should be hidden and it is already hidden, then noop + if (!visible && !mNotificationShown && !force) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + Message message; + if (visible) { + + // Not enough time has passed to show the notification again + if (System.currentTimeMillis() < mNotificationRepeatTime) { + return; + } + + if (mNotification == null) { + // Cache the Notification mainly so we can remove the + // EVENT_NOTIFICATION_CHANGED message with this Notification from + // the queue later + mNotification = new Notification(); + mNotification.when = 0; + mNotification.icon = ICON_NETWORKS_AVAILABLE; + mNotification.flags = Notification.FLAG_AUTO_CANCEL; + mNotification.contentIntent = PendingIntent.getActivity(mContext, 0, + new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0); + } + + CharSequence title = mContext.getResources().getQuantityText( + com.android.internal.R.plurals.wifi_available, numNetworks); + CharSequence details = mContext.getResources().getQuantityText( + com.android.internal.R.plurals.wifi_available_detailed, numNetworks); + mNotification.tickerText = title; + mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); + + mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; + + notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification); + /* + * TODO: Clean up connectivity service & remove this + */ + /* message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, + ICON_NETWORKS_AVAILABLE, mNotification); */ + + + } else { + + notificationManager.cancel(ICON_NETWORKS_AVAILABLE); + /* + * TODO: Clean up connectivity service & remove this + */ + /* + // Remove any pending messages to show the notification + mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); + + message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, + ICON_NETWORKS_AVAILABLE); + */ + } + + //mCsHandler.sendMessageDelayed(message, delay); + + mNotificationShown = visible; + } + + private class NotificationEnabledSettingObserver extends ContentObserver { + + public NotificationEnabledSettingObserver(Handler handler) { + super(handler); + } + + public void register() { + ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); + mNotificationEnabled = getValue(); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + mNotificationEnabled = getValue(); + resetNotification(); + } + + private boolean getValue() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; + } + } + + } diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index be14cd3..46d6bef 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -27,7 +27,6 @@ import android.net.DhcpInfo; import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wifi.WifiStateTracker; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -77,7 +76,6 @@ public class WifiWatchdogService { private Context mContext; private ContentResolver mContentResolver; - private WifiStateTracker mWifiStateTracker; private WifiManager mWifiManager; /** @@ -108,10 +106,9 @@ public class WifiWatchdogService { /** Whether the current AP check should be canceled. */ private boolean mShouldCancel; - WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) { + WifiWatchdogService(Context context) { mContext = context; mContentResolver = context.getContentResolver(); - mWifiStateTracker = wifiStateTracker; mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); createThread(); @@ -752,7 +749,7 @@ public class WifiWatchdogService { // Black list this "bad" AP, this will cause an attempt to connect to another blacklistAp(ap.bssid); // Initiate an association to an alternate AP - mWifiStateTracker.reassociateCommand(); + mWifiManager.reassociate(); } private void blacklistAp(String bssid) { @@ -763,7 +760,7 @@ public class WifiWatchdogService { // Before taking action, make sure we should not cancel our processing if (shouldCancel()) return; - mWifiStateTracker.addToBlacklist(bssid); + mWifiManager.addToBlacklist(bssid); if (D) { myLogD("Blacklisting " + bssid); @@ -858,7 +855,7 @@ public class WifiWatchdogService { * (and blacklisted them). Clear the blacklist so the AP with best * signal is chosen. */ - mWifiStateTracker.clearBlacklist(); + mWifiManager.clearBlacklist(); if (V) { myLogV("handleSleep: Set state to SLEEP and cleared blacklist"); @@ -929,7 +926,7 @@ public class WifiWatchdogService { * should revert anything done by the watchdog monitoring. */ private void handleReset() { - mWifiStateTracker.clearBlacklist(); + mWifiManager.clearBlacklist(); setIdleState(true); } diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp index 5f83636..e8b2ebf 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -495,7 +495,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const const ISurface::BufferHeap& buffers(mBufferHeap); uint32_t w = mLayer.mTransformedBounds.width(); uint32_t h = mLayer.mTransformedBounds.height(); - if (buffers.w * h != buffers.h * w) { + if (mLayer.getOrientation() & (Transform::ROT_90 | Transform::ROT_270)) { int t = w; w = h; h = t; } diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 63bdb99..2934e6a 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -215,7 +215,7 @@ public final class CallManager { * @param phone */ public void unregisterPhone(Phone phone) { - if (phone != null && !mPhones.contains(phone)) { + if (phone != null && mPhones.contains(phone)) { mPhones.remove(phone); mRingingCalls.remove(phone.getRingingCall()); mBackgroundCalls.remove(phone.getBackgroundCall()); diff --git a/telephony/java/com/android/internal/telephony/cat/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java index 0ba3e11..2eb6ccb 100644 --- a/telephony/java/com/android/internal/telephony/cat/AppInterface.java +++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java @@ -58,7 +58,8 @@ public interface AppInterface { SET_UP_EVENT_LIST(0x05), SET_UP_IDLE_MODE_TEXT(0x28), SET_UP_MENU(0x25), - SET_UP_CALL(0x10); + SET_UP_CALL(0x10), + PROVIDE_LOCAL_INFORMATION(0x26); private int mValue; diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java index b916713..1e23e34 100644 --- a/telephony/java/com/android/internal/telephony/cat/CatService.java +++ b/telephony/java/com/android/internal/telephony/cat/CatService.java @@ -22,6 +22,7 @@ import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import android.os.SystemProperties; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.CommandsInterface; @@ -245,48 +246,46 @@ public class CatService extends Handler implements AppInterface { CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); switch (cmdParams.getCommandType()) { - case SET_UP_MENU: - if (removeMenu(cmdMsg.getMenu())) { - mMenuCmd = null; - } else { - mMenuCmd = cmdMsg; - } - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, - null); - break; - case DISPLAY_TEXT: - // when application is not required to respond, send an immediate - // response. - if (!cmdMsg.geTextMessage().responseNeeded) { - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, - 0, null); - } - break; - case REFRESH: - // ME side only handles refresh commands which meant to remove IDLE - // MODE TEXT. - cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT - .value(); - break; - case SET_UP_IDLE_MODE_TEXT: - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, - 0, null); - break; - case LAUNCH_BROWSER: - case SELECT_ITEM: - case GET_INPUT: - case GET_INKEY: - case SEND_DTMF: - case SEND_SMS: - case SEND_SS: - case SEND_USSD: - case PLAY_TONE: - case SET_UP_CALL: - // nothing to do on telephony! - break; - default: - CatLog.d(this, "Unsupported command"); - return; + case SET_UP_MENU: + if (removeMenu(cmdMsg.getMenu())) { + mMenuCmd = null; + } else { + mMenuCmd = cmdMsg; + } + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + break; + case DISPLAY_TEXT: + // when application is not required to respond, send an immediate response. + if (!cmdMsg.geTextMessage().responseNeeded) { + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + } + break; + case REFRESH: + // ME side only handles refresh commands which meant to remove IDLE + // MODE TEXT. + cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value(); + break; + case SET_UP_IDLE_MODE_TEXT: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + break; + case PROVIDE_LOCAL_INFORMATION: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + return; + case LAUNCH_BROWSER: + case SELECT_ITEM: + case GET_INPUT: + case GET_INKEY: + case SEND_DTMF: + case SEND_SMS: + case SEND_SS: + case SEND_USSD: + case PLAY_TONE: + case SET_UP_CALL: + // nothing to do on telephony! + break; + default: + CatLog.d(this, "Unsupported command"); + return; } mCurrntCmd = cmdMsg; Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); @@ -315,6 +314,11 @@ public class CatService extends Handler implements AppInterface { } ByteArrayOutputStream buf = new ByteArrayOutputStream(); + Input cmdInput = null; + if (mCurrntCmd != null) { + cmdInput = mCurrntCmd.geInput(); + } + // command details int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); if (cmdDet.compRequired) { @@ -327,7 +331,13 @@ public class CatService extends Handler implements AppInterface { buf.write(cmdDet.commandQualifier); // device identities - tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); + // According to TS102.223/TS31.111 section 6.8 Structure of + // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N, + // the ME should set the CR(comprehension required) flag to + // comprehension not required.(CR=0)" + // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N, + // the CR flag is not set. + tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value(); buf.write(tag); buf.write(0x02); // length buf.write(DEV_ID_TERMINAL); // source device id @@ -348,6 +358,8 @@ public class CatService extends Handler implements AppInterface { // Fill optional data for each corresponding command if (resp != null) { resp.format(buf); + } else { + encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); } byte[] rawData = buf.toByteArray(); @@ -359,6 +371,52 @@ public class CatService extends Handler implements AppInterface { mCmdIf.sendTerminalResponse(hexString, null); } + private void encodeOptionalTags(CommandDetails cmdDet, + ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) { + switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) { + case GET_INKEY: + // ETSI TS 102 384,27.22.4.2.8.4.2. + // If it is a response for GET_INKEY command and the response timeout + // occured, then add DURATION TLV for variable timeout case. + if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) && + (cmdInput != null) && (cmdInput.duration != null)) { + getInKeyResponse(buf, cmdInput); + } + break; + case PROVIDE_LOCAL_INFORMATION: + if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) && + (resultCode.value() == ResultCode.OK.value())) { + getPliResponse(buf); + } + break; + default: + CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand); + break; + } + } + + private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) { + int tag = ComprehensionTlvTag.DURATION.value(); + + buf.write(tag); + buf.write(0x02); // length + buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds) + buf.write(cmdInput.duration.timeInterval); // Time Duration + } + + private void getPliResponse(ByteArrayOutputStream buf) { + + // Locale Language Setting + String lang = SystemProperties.get("persist.sys.language"); + + if (lang != null) { + // tag + int tag = ComprehensionTlvTag.LANGUAGE.value(); + buf.write(tag); + ResponseData.writeLength(buf, lang.length()); + buf.write(lang.getBytes(), 0, lang.length()); + } + } private void sendMenuSelection(int menuId, boolean helpRequired) { diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java index edb2dc8..12204a0 100644 --- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java @@ -52,6 +52,9 @@ class CommandParamsFactory extends Handler { static final int REFRESH_NAA_INIT = 0x03; static final int REFRESH_UICC_RESET = 0x04; + // Command Qualifier values for PLI command + static final int LANGUAGE_SETTING = 0x04; + static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, IccFileHandler fh) { if (sInstance != null) { @@ -112,7 +115,10 @@ class CommandParamsFactory extends Handler { AppInterface.CommandType cmdType = AppInterface.CommandType .fromInt(cmdDet.typeOfCommand); if (cmdType == null) { - sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + // This PROACTIVE COMMAND is presently not handled. Hence set + // result code as BEYOND_TERMINAL_CAPABILITY in TR. + mCmdParams = new CommandParams(cmdDet); + sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); return; } @@ -155,10 +161,13 @@ class CommandParamsFactory extends Handler { case PLAY_TONE: cmdPending = processPlayTone(cmdDet, ctlvs); break; + case PROVIDE_LOCAL_INFORMATION: + cmdPending = processProvideLocalInfo(cmdDet, ctlvs); + break; default: // unsupported proactive commands mCmdParams = new CommandParams(cmdDet); - sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); return; } } catch (ResultException e) { @@ -380,6 +389,12 @@ class CommandParamsFactory extends Handler { iconId = ValueParser.retrieveIconId(ctlv); } + // parse duration + ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); + if (ctlv != null) { + input.duration = ValueParser.retrieveDuration(ctlv); + } + input.minLen = 1; input.maxLen = 1; @@ -863,4 +878,20 @@ class CommandParamsFactory extends Handler { } return false; } + + private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) + throws ResultException { + CatLog.d(this, "process ProvideLocalInfo"); + switch (cmdDet.commandQualifier) { + case LANGUAGE_SETTING: + CatLog.d(this, "PLI [LANGUAGE_SETTING]"); + mCmdParams = new CommandParams(cmdDet); + break; + default: + CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported"); + mCmdParams = new CommandParams(cmdDet); + throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY); + } + return false; + } } diff --git a/telephony/java/com/android/internal/telephony/cat/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java index 8bcaab9..13a5ad4 100644 --- a/telephony/java/com/android/internal/telephony/cat/Input.java +++ b/telephony/java/com/android/internal/telephony/cat/Input.java @@ -36,6 +36,7 @@ public class Input implements Parcelable { public boolean echo; public boolean yesNo; public boolean helpAvailable; + public Duration duration; Input() { text = ""; @@ -49,6 +50,7 @@ public class Input implements Parcelable { echo = false; yesNo = false; helpAvailable = false; + duration = null; } private Input(Parcel in) { @@ -63,6 +65,7 @@ public class Input implements Parcelable { echo = in.readInt() == 1 ? true : false; yesNo = in.readInt() == 1 ? true : false; helpAvailable = in.readInt() == 1 ? true : false; + duration = in.readParcelable(null); } public int describeContents() { @@ -81,6 +84,7 @@ public class Input implements Parcelable { dest.writeInt(echo ? 1 : 0); dest.writeInt(yesNo ? 1 : 0); dest.writeInt(helpAvailable ? 1 : 0); + dest.writeParcelable(duration, 0); } public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() { diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java index 84c08f8..677d66b 100644 --- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java +++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java @@ -28,6 +28,16 @@ abstract class ResponseData { * the ByteArrayOutputStream object. */ public abstract void format(ByteArrayOutputStream buf); + + public static void writeLength(ByteArrayOutputStream buf, int length) { + // As per ETSI 102.220 Sec7.1.2, if the total length is greater + // than 0x7F, it should be coded in two bytes and the first byte + // should be 0x81. + if (length > 0x7F) { + buf.write(0x81); + } + buf.write(length); + } } class SelectItemResponseData extends ResponseData { @@ -120,7 +130,7 @@ class GetInkeyInputResponseData extends ResponseData { } // length - one more for data coding scheme. - buf.write(data.length + 1); + writeLength(buf, data.length + 1); // data coding scheme if (mIsUcs2) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 876c223..2ae5a3c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -974,7 +974,9 @@ public class GSMPhone extends PhoneBase { } public void getCallWaiting(Message onComplete) { - mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); + //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service + //class parameter in call waiting interrogation to network + mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); } public void setCallWaiting(boolean enable, Message onComplete) { diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk index 948ad72..eddbb4b 100644 --- a/tests/DumpRenderTree2/Android.mk +++ b/tests/DumpRenderTree2/Android.mk @@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_JAVA_LIBRARIES := android.test.runner + LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch LOCAL_PACKAGE_NAME := DumpRenderTree2 diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml index e4bfd59..dd0c4e9 100644 --- a/tests/DumpRenderTree2/AndroidManifest.xml +++ b/tests/DumpRenderTree2/AndroidManifest.xml @@ -16,6 +16,8 @@ limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2"> <application> + <uses-library android:name="android.test.runner" /> + <activity android:name=".ui.DirListActivity" android:label="Dump Render Tree 2" android:configChanges="orientation"> @@ -45,6 +47,10 @@ limitations under the License. </service> </application> + <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner" + android:targetPackage="com.android.dumprendertree2" + android:label="Layout tests script runner" /> + <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_SDCARD" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py new file mode 100644 index 0000000..b13d8c9 --- /dev/null +++ b/tests/DumpRenderTree2/assets/run_layout_tests.py @@ -0,0 +1,65 @@ +#!/usr/bin/python + +"""Run layout tests on the device. + + It runs the specified tests on the device, downloads the summaries to the temporary directory + and opens html details in the default browser. + + Usage: + run_layout_tests.py PATH +""" + +import sys +import os +import subprocess +import logging +import webbrowser +import tempfile + +#TODO: These should not be hardcoded +RESULTS_ABSOLUTE_PATH = "/sdcard/android/LayoutTests-results/" +DETAILS_HTML = "details.html" +SUMMARY_TXT = "summary.txt" + +def main(): + if len(sys.argv) > 1: + path = sys.argv[1] + else: + path = "" + + logging.basicConfig(level=logging.INFO, format='%(message)s') + + tmpdir = tempfile.gettempdir() + + # Run the tests in path + cmd = "adb shell am instrument " + cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests " + cmd += "-e path \"" + path + "\" " + cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner" + + logging.info("Running the tests...") + subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + + logging.info("Downloading the summaries...") + + # Download the txt summary to tmp folder + summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT) + cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path + subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + + # Download the html summary to tmp folder + details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML) + cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path + subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + + # Print summary to console + logging.info("All done.\n") + cmd = "cat " + summary_txt_tmp_path + os.system(cmd) + logging.info("") + + # Open the browser with summary + webbrowser.open(details_html_tmp_path) + +if __name__ == "__main__": + main(); diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java index a8695a0..7ec3dd7 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java @@ -82,6 +82,7 @@ public class ManagerService extends Service { public void handleMessage(Message msg) { switch (msg.what) { case MSG_FIRST_TEST: + mSummarizer.reset(); Bundle bundle = msg.getData(); ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index")); break; diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java index d9e7ec0..e27ecc9 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java @@ -166,7 +166,7 @@ public class Summarizer { "</script>"; /** TODO: Make it a setting */ - private static final String HTML_SUMMARY_RELATIVE_PATH = "summary.html"; + private static final String HTML_DETAILS_RELATIVE_PATH = "details.html"; private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt"; private int mCrashedTestsCount = 0; @@ -201,10 +201,18 @@ public class Summarizer { } public void summarize() { - createHtmlSummary(); + createHtmlDetails(); createTxtSummary(); } + public void reset() { + mCrashedTestsCount = 0; + mFailedNotIgnoredTests.clear(); + mIgnoredTests.clear(); + mPassedNotIgnoredTests.clear(); + mTitleString = null; + } + private void createTxtSummary() { StringBuilder txt = new StringBuilder(); @@ -221,7 +229,7 @@ public class Summarizer { txt.toString().getBytes(), false); } - private void createHtmlSummary() { + private void createHtmlDetails() { StringBuilder html = new StringBuilder(); html.append("<html><head>"); @@ -239,7 +247,7 @@ public class Summarizer { html.append("</body></html>"); - FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_SUMMARY_RELATIVE_PATH), + FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH), html.toString().getBytes(), false); } diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java index 982fe10..c95199f 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java @@ -24,6 +24,8 @@ import android.os.Handler; import android.os.Message; import android.view.Window; +import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback; + import java.util.ArrayList; /** @@ -57,6 +59,9 @@ public class TestsListActivity extends Activity { private ArrayList<String> mTestsList; private int mTotalTestCount; + private OnEverythingFinishedCallback mOnEverythingFinishedCallback; + private boolean mEverythingFinished; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -110,8 +115,19 @@ public class TestsListActivity extends Activity { } } + public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) { + mOnEverythingFinishedCallback = callback; + if (mEverythingFinished) { + mOnEverythingFinishedCallback.onFinished(); + } + } + private void onEverythingFinishedIntent(Intent intent) { /** TODO: Show some kind of summary to the user */ + mEverythingFinished = true; + if (mOnEverythingFinishedCallback != null) { + mOnEverythingFinishedCallback.onFinished(); + } } @Override diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java index f76105d..2145af7 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java @@ -68,15 +68,15 @@ public class TestsListPreloaderThread extends Thread { File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath); if (!file.exists()) { Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath); - return; - } - - /** Populate the tests' list accordingly */ - if (file.isDirectory()) { - preloadTests(mRelativePath); } else { - mTestsList.add(mRelativePath); + /** Populate the tests' list accordingly */ + if (file.isDirectory()) { + preloadTests(mRelativePath); + } else { + mTestsList.add(mRelativePath); + } } + mDoneMsg.obj = mTestsList; mDoneMsg.sendToTarget(); } diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java new file mode 100644 index 0000000..e1d4364 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.scriptsupport; + +/** + * Callback used to inform scriptsupport.Starter that everything is finished and + * we can exit + */ +public interface OnEverythingFinishedCallback { + public void onFinished(); +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java new file mode 100644 index 0000000..78f58d5 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.scriptsupport; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; + +/** + * Extends InstrumentationTestRunner to allow the script to pass arguments to the application + */ +public class ScriptTestRunner extends InstrumentationTestRunner { + String mTestsRelativePath; + + @Override + public void onCreate(Bundle arguments) { + mTestsRelativePath = arguments.getString("path"); + super.onCreate(arguments); + } + + public String getTestsRelativePath() { + return mTestsRelativePath; + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java new file mode 100644 index 0000000..ddfae69 --- /dev/null +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dumprendertree2.scriptsupport; + +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import com.android.dumprendertree2.TestsListActivity; + +/** + * A class which provides methods that can be invoked by a script running on the host machine to + * run the tests. + * + * It starts a TestsListActivity and does not return until all the tests finish executing. + */ +public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> { + private static final String LOG_TAG = "Starter"; + private boolean mEverythingFinished; + + public Starter() { + super(TestsListActivity.class); + } + + /** + * This method is called from adb to start executing the tests. It doesn't return + * until everything is finished so that the script can wait for the end if it needs + * to. + */ + public void startLayoutTests() { + ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation(); + String relativePath = runner.getTestsRelativePath(); + + Intent intent = new Intent(); + intent.setClassName("com.android.dumprendertree2", "TestsListActivity"); + intent.setAction(Intent.ACTION_RUN); + intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath); + setActivityIntent(intent); + getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() { + /** This method is safe to call on any thread */ + @Override + public void onFinished() { + synchronized (Starter.this) { + mEverythingFinished = true; + Starter.this.notifyAll(); + } + } + }); + + synchronized (this) { + while (!mEverythingFinished) { + try { + this.wait(); + } catch (InterruptedException e) { + Log.e(LOG_TAG + "::startLayoutTests", e.getMessage()); + } + } + } + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java index 661a8ec..af0d7d1 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java @@ -378,6 +378,10 @@ public class DirListActivity extends ListActivity { private ListItem[] getDirList(String dirPath) { File dir = new File(mRootDirPath, dirPath); + if (!dir.exists()) { + return new ListItem[0]; + } + List<ListItem> subDirs = new ArrayList<ListItem>(); List<ListItem> subFiles = new ArrayList<ListItem>(); diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index b339a2c..094b7db 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -41,7 +41,7 @@ LOCAL_STATIC_LIBRARIES := \ libpng ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt +LOCAL_LDLIBS += -lrt -lpthread endif # Statically link libz for MinGW (Win SDK under Linux), diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk index ab79f8d..f284e86 100644 --- a/tools/localize/Android.mk +++ b/tools/localize/Android.mk @@ -34,7 +34,7 @@ LOCAL_STATIC_LIBRARIES := \ libcutils ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt +LOCAL_LDLIBS += -lrt -lpthread endif diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk new file mode 100644 index 0000000..b02c1cb --- /dev/null +++ b/tools/obbtool/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright 2010 The Android Open Source Project +# +# Opaque Binary Blob (OBB) Tool +# + +# This tool is prebuilt if we're doing an app-only build. +ifeq ($(TARGET_BUILD_APPS),) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + Main.cpp + +#LOCAL_C_INCLUDES += + +LOCAL_STATIC_LIBRARIES := \ + libutils \ + libcutils + +ifeq ($(HOST_OS),linux) +LOCAL_LDLIBS += -lpthread +endif + +LOCAL_MODULE := obbtool + +include $(BUILD_HOST_EXECUTABLE) + +endif # TARGET_BUILD_APPS diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp new file mode 100644 index 0000000..2a9bf04 --- /dev/null +++ b/tools/obbtool/Main.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2010 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. + */ + +#include <utils/ObbFile.h> +#include <utils/String8.h> + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> + +using namespace android; + +static const char* gProgName = "obbtool"; +static const char* gProgVersion = "1.0"; + +static int wantUsage = 0; +static int wantVersion = 0; + +#define ADD_OPTS "n:v:f:c:" +static const struct option longopts[] = { + {"help", no_argument, &wantUsage, 1}, + {"version", no_argument, &wantVersion, 1}, + + /* Args for "add" */ + {"name", required_argument, NULL, 'n'}, + {"version", required_argument, NULL, 'v'}, + {"filesystem", required_argument, NULL, 'f'}, + {"crypto", required_argument, NULL, 'c'}, + + {NULL, 0, NULL, '\0'} +}; + +struct package_info_t { + char* packageName; + int packageVersion; + char* filesystem; + char* crypto; +}; + +/* + * Print usage info. + */ +void usage(void) +{ + fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n"); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, + " %s a[dd] [ OPTIONS ] FILENAME\n" + " Adds an OBB signature to the file.\n\n", gProgName); + fprintf(stderr, + " %s r[emove] FILENAME\n" + " Removes the OBB signature from the file.\n\n", gProgName); + fprintf(stderr, + " %s i[nfo] FILENAME\n" + " Prints the OBB signature information of a file.\n\n", gProgName); +} + +void doAdd(const char* filename, struct package_info_t* info) { + ObbFile *obb = new ObbFile(); + if (obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename); + return; + } + + obb->setPackageName(String8(info->packageName)); + obb->setVersion(info->packageVersion); + + if (!obb->writeTo(filename)) { + fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n", + filename, strerror(errno)); + return; + } + + fprintf(stderr, "OBB signature successfully written\n"); +} + +void doRemove(const char* filename) { + ObbFile *obb = new ObbFile(); + if (!obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename); + return; + } + + if (!obb->removeFrom(filename)) { + fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename); + return; + } + + fprintf(stderr, "OBB signature successfully removed\n"); +} + +void doInfo(const char* filename) { + ObbFile *obb = new ObbFile(); + if (!obb->readFrom(filename)) { + fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename); + return; + } + + printf("OBB info for '%s':\n", filename); + printf("Package name: %s\n", obb->getPackageName().string()); + printf(" Version: %d\n", obb->getVersion()); +} + +/* + * Parse args. + */ +int main(int argc, char* const argv[]) +{ + const char *prog = argv[0]; + struct options *options; + int opt; + int option_index = 0; + struct package_info_t package_info; + + int result = 1; // pessimistically assume an error. + + if (argc < 2) { + wantUsage = 1; + goto bail; + } + + while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) { + switch (opt) { + case 0: + if (longopts[option_index].flag) + break; + fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name); + wantUsage = 1; + goto bail; + case 'n': + package_info.packageName = optarg; + break; + case 'v': + char *end; + package_info.packageVersion = strtol(optarg, &end, 10); + if (*optarg == '\0' || *end != '\0') { + fprintf(stderr, "ERROR: invalid version; should be integer!\n\n"); + wantUsage = 1; + goto bail; + } + break; + case 'f': + package_info.filesystem = optarg; + break; + case 'c': + package_info.crypto = optarg; + break; + case '?': + wantUsage = 1; + goto bail; + } + } + + if (wantVersion) { + fprintf(stderr, "%s %s\n", gProgName, gProgVersion); + } + + if (wantUsage) { + goto bail; + } + +#define CHECK_OP(name) \ + if (strncmp(op, name, opsize)) { \ + fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \ + wantUsage = 1; \ + goto bail; \ + } + + if (optind < argc) { + const char* op = argv[optind++]; + const int opsize = strlen(op); + + if (optind >= argc) { + fprintf(stderr, "ERROR: filename required!\n\n"); + wantUsage = 1; + goto bail; + } + + const char* filename = argv[optind++]; + + switch (op[0]) { + case 'a': + CHECK_OP("add"); + if (package_info.packageName == NULL) { + fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n"); + goto bail; + } + doAdd(filename, &package_info); + break; + case 'r': + CHECK_OP("remove"); + doRemove(filename); + break; + case 'i': + CHECK_OP("info"); + doInfo(filename); + break; + default: + fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op); + wantUsage = 1; + goto bail; + } + } + +bail: + if (wantUsage) { + usage(); + result = 2; + } + + return result; +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 6e0bc9d..3426af7 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -85,5 +85,13 @@ interface IWifiManager WifiConfiguration getWifiApConfiguration(); void setWifiApConfiguration(in WifiConfiguration wifiConfig); + + void startWifi(); + + void stopWifi(); + + void addToBlacklist(String bssid); + + void clearBlacklist(); } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 6fac902..339763a 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -293,6 +293,21 @@ public class WifiManager { public static final String EXTRA_NEW_RSSI = "newRssi"; /** + * Broadcast intent action indicating that the IP configuration + * changed on wifi. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED"; + /** + * The lookup key for a {@link android.net.NetworkProperties} object associated with the + * Wi-Fi network. Retrieve with + * {@link android.content.Intent#getParcelableExtra(String)}. + * @hide + */ + public static final String EXTRA_NETWORK_PROPERTIES = "networkProperties"; + + /** * The network IDs of the configured networks could have changed. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -838,6 +853,82 @@ public class WifiManager { } } + /** + * Start the driver and connect to network. + * + * This function will over-ride WifiLock and device idle status. For example, + * even if the device is idle or there is only a scan-only lock held, + * a start wifi would mean that wifi connection is kept active until + * a stopWifi() is sent. + * + * This API is used by WifiStateTracker + * + * @return {@code true} if the operation succeeds else {@code false} + * @hide + */ + public boolean startWifi() { + try { + mService.startWifi(); + return true; + } catch (RemoteException e) { + return false; + } + } + + /** + * Disconnect from a network (if any) and stop the driver. + * + * This function will over-ride WifiLock and device idle status. Wi-Fi + * stays inactive until a startWifi() is issued. + * + * This API is used by WifiStateTracker + * + * @return {@code true} if the operation succeeds else {@code false} + * @hide + */ + public boolean stopWifi() { + try { + mService.stopWifi(); + return true; + } catch (RemoteException e) { + return false; + } + } + + /** + * Add a bssid to the supplicant blacklist + * + * This API is used by WifiWatchdogService + * + * @return {@code true} if the operation succeeds else {@code false} + * @hide + */ + public boolean addToBlacklist(String bssid) { + try { + mService.addToBlacklist(bssid); + return true; + } catch (RemoteException e) { + return false; + } + } + + /** + * Clear the supplicant blacklist + * + * This API is used by WifiWatchdogService + * + * @return {@code true} if the operation succeeds else {@code false} + * @hide + */ + public boolean clearBlacklist() { + try { + mService.clearBlacklist(); + return true; + } catch (RemoteException e) { + return false; + } + } + /** * Allows an application to keep the Wi-Fi radio awake. * Normally the Wi-Fi radio may turn off when the user has not used the device in a while. diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index f2f8343..af3132f 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -19,14 +19,13 @@ package android.net.wifi; import android.util.Log; import android.util.Config; import android.net.NetworkInfo; -import android.net.NetworkStateTracker; 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 WifiStateTracker} for handling. Runs in its own thread. + * to the {@link WifiStateMachine} for handling. Runs in its own thread. * * @hide */ @@ -117,7 +116,7 @@ public class WifiMonitor { private static Pattern mConnectedEventPattern = Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) "); - private final WifiStateTracker mWifiStateTracker; + private final WifiStateMachine mWifiStateMachine; /** * This indicates the supplicant connection for the monitor is closed @@ -139,18 +138,14 @@ public class WifiMonitor { */ private static final int MAX_RECV_ERRORS = 10; - public WifiMonitor(WifiStateTracker tracker) { - mWifiStateTracker = tracker; + public WifiMonitor(WifiStateMachine wifiStateMachine) { + mWifiStateMachine = wifiStateMachine; } public void startMonitoring() { new MonitorThread().start(); } - public NetworkStateTracker getNetworkStateTracker() { - return mWifiStateTracker; - } - class MonitorThread extends Thread { public MonitorThread() { super("WifiMonitor"); @@ -161,9 +156,9 @@ public class WifiMonitor { if (connectToSupplicant()) { // Send a message indicating that it is now possible to send commands // to the supplicant - mWifiStateTracker.notifySupplicantConnection(); + mWifiStateMachine.notifySupplicantConnection(); } else { - mWifiStateTracker.notifySupplicantLost(); + mWifiStateMachine.notifySupplicantLost(); return; } @@ -259,7 +254,7 @@ public class WifiMonitor { } // notify and exit - mWifiStateTracker.notifySupplicantLost(); + mWifiStateMachine.notifySupplicantLost(); break; } else { handleEvent(event, eventData); @@ -285,7 +280,7 @@ public class WifiMonitor { } private void handlePasswordKeyMayBeIncorrect() { - mWifiStateTracker.notifyPasswordKeyMayBeIncorrect(); + mWifiStateMachine.notifyPasswordKeyMayBeIncorrect(); } private void handleDriverEvent(String state) { @@ -293,11 +288,11 @@ public class WifiMonitor { return; } if (state.equals("STOPPED")) { - mWifiStateTracker.notifyDriverStopped(); + mWifiStateMachine.notifyDriverStopped(); } else if (state.equals("STARTED")) { - mWifiStateTracker.notifyDriverStarted(); + mWifiStateMachine.notifyDriverStarted(); } else if (state.equals("HANGED")) { - mWifiStateTracker.notifyDriverHung(); + mWifiStateMachine.notifyDriverHung(); } } @@ -318,7 +313,7 @@ public class WifiMonitor { break; case SCAN_RESULTS: - mWifiStateTracker.notifyScanResultsAvailable(); + mWifiStateMachine.notifyScanResultsAvailable(); break; case UNKNOWN: @@ -375,7 +370,7 @@ public class WifiMonitor { if (newSupplicantState == SupplicantState.INVALID) { Log.w(TAG, "Invalid supplicant state: " + newState); } - mWifiStateTracker.notifySupplicantStateChange(networkId, BSSID, newSupplicantState); + mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState); } } @@ -395,7 +390,7 @@ public class WifiMonitor { } } } - mWifiStateTracker.notifyNetworkStateChange(newState, BSSID, networkId); + mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId); } /** diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java new file mode 100644 index 0000000..845508b --- /dev/null +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -0,0 +1,3571 @@ +/* + * Copyright (C) 2010 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; + +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; + +/** + * TODO: Add soft AP states as part of WIFI_STATE_XXX + * Retain WIFI_STATE_ENABLING that indicates driver is loading + * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started + * and WIFI_STATE_FAILED for failure + * Deprecate WIFI_STATE_UNKNOWN + * + * Doing this will simplify the logic for sending broadcasts + */ +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; + +import android.app.ActivityManagerNative; +import android.net.NetworkInfo; +import android.net.DhcpInfo; +import android.net.NetworkUtils; +import android.net.ConnectivityManager; +import android.net.NetworkInfo.DetailedState; +import android.net.NetworkProperties; +import android.os.Binder; +import android.os.Message; +import android.os.Parcelable; +import android.os.Handler; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.PowerManager; +import android.os.SystemProperties; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Process; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.app.backup.IBackupManager; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothA2dp; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.Context; +import android.database.ContentObserver; +import com.android.internal.app.IBatteryStats; +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; + +/** + * Track the state of Wifi connectivity. All event handling is done here, + * and all changes in connectivity state are initiated here. + * + * @hide + */ +//TODO: we still need frequent scanning for the case when +// we issue disconnect but need scan results for open network notification +public class WifiStateMachine extends HierarchicalStateMachine { + + private static final String TAG = "WifiStateMachine"; + private static final String NETWORKTYPE = "WIFI"; + private static final boolean DBG = false; + + /* TODO: fetch a configurable interface */ + private static final String SOFTAP_IFACE = "wl0.1"; + + private WifiMonitor mWifiMonitor; + private INetworkManagementService nwService; + private ConnectivityManager mCm; + + /* Scan results handling */ + private List<ScanResult> mScanResults; + private static final Pattern scanResultPattern = Pattern.compile("\t+"); + private static final int SCAN_RESULT_CACHE_SIZE = 80; + private final LinkedHashMap<String, ScanResult> mScanResultCache; + + private String mInterfaceName; + + private int mNumAllowedChannels = 0; + private int mLastSignalLevel = -1; + private String mLastBssid; + private int mLastNetworkId; + private boolean mEnableRssiPolling = false; + private boolean mPasswordKeyMayBeIncorrect = false; + private boolean mUseStaticIp = false; + private int mReconnectCount = 0; + private boolean mIsScanMode = false; + private boolean mConfigChanged = false; + + /** + * Instance of the bluetooth headset helper. This needs to be created + * early because there is a delay before it actually 'connects', as + * noted by its javadoc. If we check before it is connected, it will be + * in an error state and we will not disable coexistence. + */ + private BluetoothHeadset mBluetoothHeadset; + + private BluetoothA2dp mBluetoothA2dp; + + /** + * Observes the static IP address settings. + */ + private SettingsObserver mSettingsObserver; + private NetworkProperties mNetworkProperties; + + // Held during driver load and unload + private static PowerManager.WakeLock sWakeLock; + + private Context mContext; + + private DhcpInfo mDhcpInfo; + private WifiInfo mWifiInfo; + private NetworkInfo mNetworkInfo; + private SupplicantStateTracker mSupplicantStateTracker; + + // 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; + private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023; + + /* Load the driver */ + private static final int CMD_LOAD_DRIVER = 1; + /* Unload the driver */ + private static final int CMD_UNLOAD_DRIVER = 2; + /* Indicates driver load succeeded */ + private static final int CMD_LOAD_DRIVER_SUCCESS = 3; + /* Indicates driver load failed */ + private static final int CMD_LOAD_DRIVER_FAILURE = 4; + /* Indicates driver unload succeeded */ + private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5; + /* Indicates driver unload failed */ + private static final int CMD_UNLOAD_DRIVER_FAILURE = 6; + + /* Start the supplicant */ + private static final int CMD_START_SUPPLICANT = 11; + /* Stop the supplicant */ + private static final int CMD_STOP_SUPPLICANT = 12; + /* Start the driver */ + private static final int CMD_START_DRIVER = 13; + /* Start the driver */ + private static final int CMD_STOP_DRIVER = 14; + /* Indicates DHCP succeded */ + private static final int CMD_IP_CONFIG_SUCCESS = 15; + /* Indicates DHCP failed */ + private static final int CMD_IP_CONFIG_FAILURE = 16; + /* Re-configure interface */ + private static final int CMD_RECONFIGURE_IP = 17; + + + /* Start the soft access point */ + private static final int CMD_START_AP = 21; + /* Stop the soft access point */ + private static final int CMD_STOP_AP = 22; + + + /* Supplicant events */ + /* Connection to supplicant established */ + private static final int SUP_CONNECTION_EVENT = 31; + /* Connection to supplicant lost */ + private static final int SUP_DISCONNECTION_EVENT = 32; + /* Driver start completed */ + private static final int DRIVER_START_EVENT = 33; + /* Driver stop completed */ + private static final int DRIVER_STOP_EVENT = 34; + /* Network connection completed */ + private static final int NETWORK_CONNECTION_EVENT = 36; + /* Network disconnection completed */ + private static final int NETWORK_DISCONNECTION_EVENT = 37; + /* Scan results are available */ + private static final int SCAN_RESULTS_EVENT = 38; + /* Supplicate state changed */ + private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39; + /* Password may be incorrect */ + private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40; + + /* Supplicant commands */ + /* Is supplicant alive ? */ + private static final int CMD_PING_SUPPLICANT = 51; + /* Add/update a network configuration */ + private static final int CMD_ADD_OR_UPDATE_NETWORK = 52; + /* Delete a network */ + private static final int CMD_REMOVE_NETWORK = 53; + /* Enable a network. The device will attempt a connection to the given network. */ + private static final int CMD_ENABLE_NETWORK = 54; + /* Disable a network. The device does not attempt a connection to the given network. */ + private static final int CMD_DISABLE_NETWORK = 55; + /* Blacklist network. De-prioritizes the given BSSID for connection. */ + private static final int CMD_BLACKLIST_NETWORK = 56; + /* Clear the blacklist network list */ + private static final int CMD_CLEAR_BLACKLIST = 57; + /* Get the configured networks */ + private static final int CMD_GET_NETWORK_CONFIG = 58; + /* Save configuration */ + private static final int CMD_SAVE_CONFIG = 59; + /* Connection status */ + private static final int CMD_CONNECTION_STATUS = 60; + + /* Supplicant commands after driver start*/ + /* Initiate a scan */ + private static final int CMD_START_SCAN = 71; + /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */ + private static final int CMD_SET_SCAN_MODE = 72; + /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */ + private static final int CMD_SET_SCAN_TYPE = 73; + /* Disconnect from a network */ + private static final int CMD_DISCONNECT = 74; + /* Reconnect to a network */ + private static final int CMD_RECONNECT = 75; + /* Reassociate to a network */ + private static final int CMD_REASSOCIATE = 76; + /* Set power mode + * POWER_MODE_ACTIVE + * POWER_MODE_AUTO + */ + private static final int CMD_SET_POWER_MODE = 77; + /* Set bluetooth co-existence + * BLUETOOTH_COEXISTENCE_MODE_ENABLED + * BLUETOOTH_COEXISTENCE_MODE_DISABLED + * BLUETOOTH_COEXISTENCE_MODE_SENSE + */ + private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78; + /* Enable/disable bluetooth scan mode + * true(1) + * false(0) + */ + private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79; + /* Set number of allowed channels */ + private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80; + /* Request connectivity manager wake lock before driver stop */ + private static final int CMD_REQUEST_CM_WAKELOCK = 81; + /* Enables RSSI poll */ + private static final int CMD_ENABLE_RSSI_POLL = 82; + /* RSSI poll */ + private static final int CMD_RSSI_POLL = 83; + /* Get current RSSI */ + private static final int CMD_GET_RSSI = 84; + /* Get approx current RSSI */ + private static final int CMD_GET_RSSI_APPROX = 85; + /* Get link speed on connection */ + private static final int CMD_GET_LINK_SPEED = 86; + /* Radio mac address */ + private static final int CMD_GET_MAC_ADDR = 87; + /* Set up packet filtering */ + private static final int CMD_START_PACKET_FILTERING = 88; + /* Clear packet filter */ + private static final int CMD_STOP_PACKET_FILTERING = 89; + + /** + * Interval in milliseconds between polling for connection + * status items that are not sent via asynchronous events. + * An example is RSSI (signal strength). + */ + private static final int POLL_RSSI_INTERVAL_MSECS = 3000; + + private static final int CONNECT_MODE = 1; + private static final int SCAN_ONLY_MODE = 2; + + private static final int SCAN_ACTIVE = 1; + private static final int SCAN_PASSIVE = 2; + + /** + * The maximum number of times we will retry a connection to an access point + * for which we have failed in acquiring an IP address from DHCP. A value of + * N means that we will make N+1 connection attempts in all. + * <p> + * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default + * value if a Settings value is not present. + */ + private static final int DEFAULT_MAX_DHCP_RETRIES = 9; + + private static final int DRIVER_POWER_MODE_ACTIVE = 1; + private static final int DRIVER_POWER_MODE_AUTO = 0; + + /* Default parent state */ + private HierarchicalState mDefaultState = new DefaultState(); + /* Temporary initial state */ + private HierarchicalState mInitialState = new InitialState(); + /* Unloading the driver */ + private HierarchicalState mDriverUnloadingState = new DriverUnloadingState(); + /* Loading the driver */ + private HierarchicalState mDriverUnloadedState = new DriverUnloadedState(); + /* Driver load/unload failed */ + private HierarchicalState mDriverFailedState = new DriverFailedState(); + /* Driver loading */ + private HierarchicalState mDriverLoadingState = new DriverLoadingState(); + /* Driver loaded */ + private HierarchicalState mDriverLoadedState = new DriverLoadedState(); + /* Driver loaded, waiting for supplicant to start */ + private HierarchicalState mWaitForSupState = new WaitForSupState(); + + /* Driver loaded and supplicant ready */ + private HierarchicalState mDriverSupReadyState = new DriverSupReadyState(); + /* Driver start issued, waiting for completed event */ + private HierarchicalState mDriverStartingState = new DriverStartingState(); + /* Driver started */ + private HierarchicalState mDriverStartedState = new DriverStartedState(); + /* Driver stopping */ + private HierarchicalState mDriverStoppingState = new DriverStoppingState(); + /* Driver stopped */ + private HierarchicalState mDriverStoppedState = new DriverStoppedState(); + /* Scan for networks, no connection will be established */ + private HierarchicalState mScanModeState = new ScanModeState(); + /* Connecting to an access point */ + private HierarchicalState mConnectModeState = new ConnectModeState(); + /* Fetching IP after network connection (assoc+auth complete) */ + private HierarchicalState mConnectingState = new ConnectingState(); + /* Connected with IP addr */ + private HierarchicalState mConnectedState = new ConnectedState(); + /* disconnect issued, waiting for network disconnect confirmation */ + private HierarchicalState mDisconnectingState = new DisconnectingState(); + /* Network is not connected, supplicant assoc+auth is not complete */ + private HierarchicalState mDisconnectedState = new DisconnectedState(); + + /* Soft Ap is running */ + private HierarchicalState mSoftApStartedState = new SoftApStartedState(); + + /* Argument for Message object to indicate a synchronous call */ + private static final int SYNCHRONOUS_CALL = 1; + private static final int ASYNCHRONOUS_CALL = 0; + + + /** + * One of {@link WifiManager#WIFI_STATE_DISABLED}, + * {@link WifiManager#WIFI_STATE_DISABLING}, + * {@link WifiManager#WIFI_STATE_ENABLED}, + * {@link WifiManager#WIFI_STATE_ENABLING}, + * {@link WifiManager#WIFI_STATE_UNKNOWN} + * + */ + private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED); + + /** + * One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + * + */ + private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED); + + private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid()); + private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid()); + + private final IBatteryStats mBatteryStats; + + public WifiStateMachine(Context context) { + super(TAG); + + mContext = context; + + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); + mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + nwService = INetworkManagementService.Stub.asInterface(b); + + mWifiMonitor = new WifiMonitor(this); + mDhcpInfo = new DhcpInfo(); + mWifiInfo = new WifiInfo(); + mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); + mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler()); + + mBluetoothHeadset = new BluetoothHeadset(mContext, null); + mNetworkProperties = new NetworkProperties(); + + mNetworkInfo.setIsAvailable(false); + mNetworkProperties.clear(); + mLastBssid = null; + mLastNetworkId = -1; + mLastSignalLevel = -1; + + mScanResultCache = new LinkedHashMap<String, ScanResult>( + SCAN_RESULT_CACHE_SIZE, 0.75f, true) { + /* + * Limit the cache size by SCAN_RESULT_CACHE_SIZE + * elements + */ + @Override + public boolean removeEldestEntry(Map.Entry eldest) { + return SCAN_RESULT_CACHE_SIZE < this.size(); + } + }; + + mSettingsObserver = new SettingsObserver(new Handler()); + + PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + + addState(mDefaultState); + addState(mInitialState, mDefaultState); + addState(mDriverUnloadingState, mDefaultState); + addState(mDriverUnloadedState, mDefaultState); + addState(mDriverFailedState, mDriverUnloadedState); + addState(mDriverLoadingState, mDefaultState); + addState(mDriverLoadedState, mDefaultState); + addState(mWaitForSupState, mDriverLoadedState); + addState(mDriverSupReadyState, mDefaultState); + addState(mDriverStartingState, mDriverSupReadyState); + addState(mDriverStartedState, mDriverSupReadyState); + addState(mScanModeState, mDriverStartedState); + addState(mConnectModeState, mDriverStartedState); + addState(mConnectingState, mConnectModeState); + addState(mConnectedState, mConnectModeState); + addState(mDisconnectingState, mConnectModeState); + addState(mDisconnectedState, mConnectModeState); + addState(mDriverStoppingState, mDriverSupReadyState); + addState(mDriverStoppedState, mDriverSupReadyState); + addState(mSoftApStartedState, mDefaultState); + + setInitialState(mInitialState); + + if (DBG) setDbg(true); + + //start the state machine + start(); + } + + /********************************************************* + * Methods exposed for public use + ********************************************************/ + + /** + * TODO: doc + */ + public boolean pingSupplicant() { + return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue; + } + + /** + * TODO: doc + */ + public boolean startScan(boolean forceActive) { + return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ? + SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue; + } + + /** + * TODO: doc + */ + public void setWifiEnabled(boolean enable) { + mLastEnableUid.set(Binder.getCallingUid()); + if (enable) { + /* Argument is the state that is entered prior to load */ + sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)); + sendMessage(CMD_START_SUPPLICANT); + } else { + sendMessage(CMD_STOP_SUPPLICANT); + /* Argument is the state that is entered upon success */ + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0)); + } + } + + /** + * TODO: doc + */ + public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) { + mLastApEnableUid.set(Binder.getCallingUid()); + if (enable) { + /* Argument is the state that is entered prior to load */ + sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0)); + sendMessage(obtainMessage(CMD_START_AP, wifiConfig)); + } else { + sendMessage(CMD_STOP_AP); + /* Argument is the state that is entered upon success */ + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0)); + } + } + + /** + * TODO: doc + */ + public int getWifiState() { + return mWifiState.get(); + } + + /** + * TODO: doc + */ + public String getWifiStateByName() { + switch (mWifiState.get()) { + case WIFI_STATE_DISABLING: + return "disabling"; + case WIFI_STATE_DISABLED: + return "disabled"; + case WIFI_STATE_ENABLING: + return "enabling"; + case WIFI_STATE_ENABLED: + return "enabled"; + case WIFI_STATE_UNKNOWN: + return "unknown state"; + default: + return "[invalid state]"; + } + } + + /** + * TODO: doc + */ + public int getWifiApState() { + return mWifiApState.get(); + } + + /** + * TODO: doc + */ + public String getWifiApStateByName() { + switch (mWifiApState.get()) { + case WIFI_AP_STATE_DISABLING: + return "disabling"; + case WIFI_AP_STATE_DISABLED: + return "disabled"; + case WIFI_AP_STATE_ENABLING: + return "enabling"; + case WIFI_AP_STATE_ENABLED: + return "enabled"; + case WIFI_AP_STATE_FAILED: + return "failed"; + default: + return "[invalid state]"; + } + } + + /** + * Get status information for the current connection, if any. + * @return a {@link WifiInfo} object containing information about the current connection + * + */ + public WifiInfo requestConnectionInfo() { + return mWifiInfo; + } + + public DhcpInfo getDhcpInfo() { + return mDhcpInfo; + } + + /** + * TODO: doc + */ + public void setDriverStart(boolean enable) { + if (enable) { + sendMessage(CMD_START_DRIVER); + } else { + sendMessage(CMD_STOP_DRIVER); + } + } + + /** + * TODO: doc + */ + public void setScanOnlyMode(boolean enable) { + if (enable) { + sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0)); + } else { + sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0)); + } + } + + /** + * TODO: doc + */ + public void setScanType(boolean active) { + if (active) { + sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0)); + } else { + sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0)); + } + } + + /** + * TODO: doc + */ + public List<ScanResult> getScanResultsList() { + return mScanResults; + } + + /** + * Disconnect from Access Point + */ + public boolean disconnectCommand() { + return sendSyncMessage(CMD_DISCONNECT).boolValue; + } + + /** + * Initiate a reconnection to AP + */ + public boolean reconnectCommand() { + return sendSyncMessage(CMD_RECONNECT).boolValue; + } + + /** + * Initiate a re-association to AP + */ + public boolean reassociateCommand() { + return sendSyncMessage(CMD_REASSOCIATE).boolValue; + } + + /** + * Add a network synchronously + * + * @return network id of the new network + */ + public int addOrUpdateNetwork(WifiConfiguration config) { + return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue; + } + + public List<WifiConfiguration> getConfiguredNetworks() { + return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList; + } + + /** + * Delete a network + * + * @param networkId id of the network to be removed + */ + public boolean removeNetwork(int networkId) { + return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue; + } + + private class EnableNetParams { + private int netId; + private boolean disableOthers; + EnableNetParams(int n, boolean b) { + netId = n; + disableOthers = b; + } + } + /** + * Enable a network + * + * @param netId network id of the network + * @param disableOthers true, if all other networks have to be disabled + * @return {@code true} if the operation succeeds, {@code false} otherwise + */ + public boolean enableNetwork(int netId, boolean disableOthers) { + return sendSyncMessage(CMD_ENABLE_NETWORK, + new EnableNetParams(netId, disableOthers)).boolValue; + } + + /** + * Disable a network + * + * @param netId network id of the network + * @return {@code true} if the operation succeeds, {@code false} otherwise + */ + public boolean disableNetwork(int netId) { + return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue; + } + + /** + * Blacklist a BSSID. This will avoid the AP if there are + * alternate APs to connect + * + * @param bssid BSSID of the network + */ + public void addToBlacklist(String bssid) { + sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid)); + } + + /** + * Clear the blacklist list + * + */ + public void clearBlacklist() { + sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST)); + } + + /** + * Get detailed status of the connection + * + * @return Example status result + * bssid=aa:bb:cc:dd:ee:ff + * ssid=TestNet + * id=3 + * pairwise_cipher=NONE + * group_cipher=NONE + * key_mgmt=NONE + * wpa_state=COMPLETED + * ip_address=X.X.X.X + */ + public String status() { + return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue; + } + + public void enableRssiPolling(boolean enabled) { + sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0)); + } + /** + * Get RSSI to currently connected network + * + * @return RSSI value, -1 on failure + */ + public int getRssi() { + return sendSyncMessage(CMD_GET_RSSI).intValue; + } + + /** + * Get approx RSSI to currently connected network + * + * @return RSSI value, -1 on failure + */ + public int getRssiApprox() { + return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue; + } + + /** + * Get link speed to currently connected network + * + * @return link speed, -1 on failure + */ + public int getLinkSpeed() { + return sendSyncMessage(CMD_GET_LINK_SPEED).intValue; + } + + /** + * Get MAC address of radio + * + * @return MAC address, null on failure + */ + public String getMacAddress() { + return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue; + } + + /** + * Start packet filtering + */ + public void startPacketFiltering() { + sendMessage(CMD_START_PACKET_FILTERING); + } + + /** + * Stop packet filtering + */ + public void stopPacketFiltering() { + sendMessage(CMD_STOP_PACKET_FILTERING); + } + + /** + * Set power mode + * @param mode + * DRIVER_POWER_MODE_AUTO + * DRIVER_POWER_MODE_ACTIVE + */ + public void setPowerMode(int mode) { + sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0)); + } + + /** + * Set the number of allowed radio frequency channels from the system + * setting value, if any. + */ + public void setNumAllowedChannels() { + try { + setNumAllowedChannels( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); + } catch (Settings.SettingNotFoundException e) { + if (mNumAllowedChannels != 0) { + setNumAllowedChannels(mNumAllowedChannels); + } + // otherwise, use the driver default + } + } + + /** + * Set the number of radio frequency channels that are allowed to be used + * in the current regulatory domain. + * @param numChannels the number of allowed channels. Must be greater than 0 + * and less than or equal to 16. + */ + public void setNumAllowedChannels(int numChannels) { + sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0)); + } + + /** + * Get number of allowed channels + * + * @return channel count, -1 on failure + * + * TODO: this is not a public API and needs to be removed in favor + * of asynchronous reporting. unused for now. + */ + public int getNumAllowedChannels() { + return -1; + } + + /** + * Set bluetooth coex mode: + * + * @param mode + * BLUETOOTH_COEXISTENCE_MODE_ENABLED + * BLUETOOTH_COEXISTENCE_MODE_DISABLED + * BLUETOOTH_COEXISTENCE_MODE_SENSE + */ + public void setBluetoothCoexistenceMode(int mode) { + sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0)); + } + + /** + * Enable or disable Bluetooth coexistence scan mode. When this mode is on, + * some of the low-level scan parameters used by the driver are changed to + * reduce interference with A2DP streaming. + * + * @param isBluetoothPlaying whether to enable or disable this mode + */ + public void setBluetoothScanMode(boolean isBluetoothPlaying) { + sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0)); + } + + /** + * Save configuration on supplicant + * + * @return {@code true} if the operation succeeds, {@code false} otherwise + * + * TODO: deprecate this + */ + public boolean saveConfig() { + return sendSyncMessage(CMD_SAVE_CONFIG).boolValue; + } + + /** + * TODO: doc + */ + public void requestCmWakeLock() { + sendMessage(CMD_REQUEST_CM_WAKELOCK); + } + + /********************************************************* + * Internal private functions + ********************************************************/ + + class SyncReturn { + boolean boolValue; + int intValue; + String stringValue; + Object objValue; + List<WifiConfiguration> configList; + } + + class SyncParams { + Object mParameter; + SyncReturn mSyncReturn; + SyncParams() { + mSyncReturn = new SyncReturn(); + } + SyncParams(Object p) { + mParameter = p; + mSyncReturn = new SyncReturn(); + } + } + + /** + * message.arg2 is reserved to indicate synchronized + * message.obj is used to store SyncParams + */ + private SyncReturn syncedSend(Message msg) { + SyncParams syncParams = (SyncParams) msg.obj; + msg.arg2 = SYNCHRONOUS_CALL; + synchronized(syncParams) { + if (DBG) Log.d(TAG, "syncedSend " + msg); + sendMessage(msg); + try { + syncParams.wait(); + } catch (InterruptedException e) { + Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()"); + return null; + } + } + return syncParams.mSyncReturn; + } + + private SyncReturn sendSyncMessage(Message msg) { + SyncParams syncParams = new SyncParams(); + msg.obj = syncParams; + return syncedSend(msg); + } + + private SyncReturn sendSyncMessage(int what, Object param) { + SyncParams syncParams = new SyncParams(param); + Message msg = obtainMessage(what, syncParams); + return syncedSend(msg); + } + + + private SyncReturn sendSyncMessage(int what) { + return sendSyncMessage(obtainMessage(what)); + } + + private void notifyOnMsgObject(Message msg) { + SyncParams syncParams = (SyncParams) msg.obj; + if (syncParams != null) { + synchronized(syncParams) { + if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg); + syncParams.notify(); + } + } + else { + Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null"); + } + } + + private void setWifiState(int wifiState) { + final int previousWifiState = mWifiState.get(); + + try { + if (wifiState == WIFI_STATE_ENABLED) { + mBatteryStats.noteWifiOn(mLastEnableUid.get()); + } else if (wifiState == WIFI_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastEnableUid.get()); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + + mWifiState.set(wifiState); + + if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName()); + + final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); + mContext.sendStickyBroadcast(intent); + } + + private void setWifiApState(int wifiApState) { + final int previousWifiApState = mWifiApState.get(); + + try { + if (wifiApState == WIFI_AP_STATE_ENABLED) { + mBatteryStats.noteWifiOn(mLastApEnableUid.get()); + } else if (wifiApState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastApEnableUid.get()); + } + } catch (RemoteException e) { + Log.d(TAG, "Failed to note battery stats in wifi"); + } + + // Update state + mWifiApState.set(wifiApState); + + if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName()); + + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); + mContext.sendStickyBroadcast(intent); + } + + /** + * Parse the scan result line passed to us by wpa_supplicant (helper). + * @param line the line to parse + * @return the {@link ScanResult} object + */ + private ScanResult parseScanResult(String line) { + ScanResult scanResult = null; + if (line != null) { + /* + * Cache implementation (LinkedHashMap) is not synchronized, thus, + * must synchronized here! + */ + synchronized (mScanResultCache) { + String[] result = scanResultPattern.split(line); + if (3 <= result.length && result.length <= 5) { + String bssid = result[0]; + // bssid | frequency | level | flags | ssid + int frequency; + int level; + try { + frequency = Integer.parseInt(result[1]); + level = Integer.parseInt(result[2]); + /* some implementations avoid negative values by adding 256 + * so we need to adjust for that here. + */ + if (level > 0) level -= 256; + } catch (NumberFormatException e) { + frequency = 0; + level = 0; + } + + /* + * The formatting of the results returned by + * wpa_supplicant is intended to make the fields + * line up nicely when printed, + * not to make them easy to parse. So we have to + * apply some heuristics to figure out which field + * is the SSID and which field is the flags. + */ + String ssid; + String flags; + if (result.length == 4) { + if (result[3].charAt(0) == '[') { + flags = result[3]; + ssid = ""; + } else { + flags = ""; + ssid = result[3]; + } + } else if (result.length == 5) { + flags = result[3]; + ssid = result[4]; + } else { + // Here, we must have 3 fields: no flags and ssid + // set + flags = ""; + ssid = ""; + } + + // bssid + ssid is the hash key + String key = bssid + ssid; + scanResult = mScanResultCache.get(key); + if (scanResult != null) { + scanResult.level = level; + scanResult.SSID = ssid; + scanResult.capabilities = flags; + scanResult.frequency = frequency; + } else { + // Do not add scan results that have no SSID set + if (0 < ssid.trim().length()) { + scanResult = + new ScanResult( + ssid, bssid, flags, level, frequency); + mScanResultCache.put(key, scanResult); + } + } + } else { + Log.w(TAG, "Misformatted scan result text with " + + result.length + " fields: " + line); + } + } + } + + return scanResult; + } + + /** + * scanResults input format + * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1 + * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2 + */ + private void setScanResults(String scanResults) { + if (scanResults == null) { + return; + } + + List<ScanResult> scanList = new ArrayList<ScanResult>(); + + int lineCount = 0; + + int scanResultsLen = scanResults.length(); + // Parse the result string, keeping in mind that the last line does + // not end with a newline. + for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) { + if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') { + ++lineCount; + + if (lineCount == 1) { + lineBeg = lineEnd + 1; + continue; + } + if (lineEnd > lineBeg) { + String line = scanResults.substring(lineBeg, lineEnd); + ScanResult scanResult = parseScanResult(line); + if (scanResult != null) { + scanList.add(scanResult); + } else { + Log.w(TAG, "misformatted scan result for: " + line); + } + } + lineBeg = lineEnd + 1; + } + } + + mScanResults = scanList; + } + + private void configureNetworkProperties() { + try { + mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName)); + } catch (SocketException e) { + Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName + + ". e=" + e); + return; + } catch (NullPointerException e) { + Log.e(TAG, "NPE creating NetworkInterface. e=" + e); + return; + } + // TODO - fix this for v6 + try { + mNetworkProperties.addAddress(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e); + } + + try { + mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray( + mDhcpInfo.gateway))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e); + } + + try { + mNetworkProperties.addDns(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.dns1))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e); + } + try { + mNetworkProperties.addDns(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.dns2))); + + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e); + } + // TODO - add proxy info + } + + + private void checkUseStaticIp() { + mUseStaticIp = false; + final ContentResolver cr = mContext.getContentResolver(); + try { + if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) { + return; + } + } catch (Settings.SettingNotFoundException e) { + return; + } + + try { + String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP); + if (addr != null) { + mDhcpInfo.ipAddress = stringToIpAddr(addr); + } else { + return; + } + addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY); + if (addr != null) { + mDhcpInfo.gateway = stringToIpAddr(addr); + } else { + return; + } + addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK); + if (addr != null) { + mDhcpInfo.netmask = stringToIpAddr(addr); + } else { + return; + } + addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1); + if (addr != null) { + mDhcpInfo.dns1 = stringToIpAddr(addr); + } else { + return; + } + addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2); + if (addr != null) { + mDhcpInfo.dns2 = stringToIpAddr(addr); + } else { + mDhcpInfo.dns2 = 0; + } + } catch (UnknownHostException e) { + return; + } + mUseStaticIp = true; + } + + private static int stringToIpAddr(String addrString) throws UnknownHostException { + try { + String[] parts = addrString.split("\\."); + if (parts.length != 4) { + throw new UnknownHostException(addrString); + } + + int a = Integer.parseInt(parts[0]) ; + int b = Integer.parseInt(parts[1]) << 8; + int c = Integer.parseInt(parts[2]) << 16; + int d = Integer.parseInt(parts[3]) << 24; + + return a | b | c | d; + } catch (NumberFormatException ex) { + throw new UnknownHostException(addrString); + } + } + + private int getMaxDhcpRetries() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT, + DEFAULT_MAX_DHCP_RETRIES); + } + + private class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); + ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_USE_STATIC_IP), false, this); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_STATIC_IP), false, this); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_STATIC_GATEWAY), false, this); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_STATIC_NETMASK), false, this); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_STATIC_DNS1), false, this); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.WIFI_STATIC_DNS2), false, this); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + boolean wasStaticIp = mUseStaticIp; + int oIp, oGw, oMsk, oDns1, oDns2; + oIp = oGw = oMsk = oDns1 = oDns2 = 0; + if (wasStaticIp) { + oIp = mDhcpInfo.ipAddress; + oGw = mDhcpInfo.gateway; + oMsk = mDhcpInfo.netmask; + oDns1 = mDhcpInfo.dns1; + oDns2 = mDhcpInfo.dns2; + } + checkUseStaticIp(); + + if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) { + return; + } + + boolean changed = + (wasStaticIp != mUseStaticIp) || + (wasStaticIp && ( + oIp != mDhcpInfo.ipAddress || + oGw != mDhcpInfo.gateway || + oMsk != mDhcpInfo.netmask || + oDns1 != mDhcpInfo.dns1 || + oDns2 != mDhcpInfo.dns2)); + + if (changed) { + sendMessage(CMD_RECONFIGURE_IP); + mConfigChanged = true; + } + } + } + + /** + * Whether to disable coexistence mode while obtaining IP address. This + * logic will return true only if the current bluetooth + * headset/handsfree state is disconnected. This means if it is in an + * error state, we will NOT disable coexistence mode to err on the side + * of safety. + * + * @return Whether to disable coexistence mode. + */ + private boolean shouldDisableCoexistenceMode() { + int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset()); + return state == BluetoothHeadset.STATE_DISCONNECTED; + } + + private void checkIsBluetoothPlaying() { + boolean isBluetoothPlaying = false; + Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks(); + + for (BluetoothDevice device : connected) { + if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) { + isBluetoothPlaying = true; + break; + } + } + setBluetoothScanMode(isBluetoothPlaying); + } + + private void sendScanResultsAvailableBroadcast() { + if (!ActivityManagerNative.isSystemReady()) return; + + mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + } + + private void sendRssiChangeBroadcast(final int newRssi) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); + intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); + mContext.sendBroadcast(intent); + } + + private void sendNetworkStateChangeBroadcast(String bssid) { + Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo); + intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties); + if (bssid != null) + intent.putExtra(WifiManager.EXTRA_BSSID, bssid); + mContext.sendStickyBroadcast(intent); + } + + private void sendConfigChangeBroadcast() { + Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION); + intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties); + mContext.sendBroadcast(intent); + } + + private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) { + Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state); + if (failedAuth) { + intent.putExtra( + WifiManager.EXTRA_SUPPLICANT_ERROR, + WifiManager.ERROR_AUTHENTICATING); + } + mContext.sendStickyBroadcast(intent); + } + + private void sendSupplicantConnectionChangedBroadcast(boolean connected) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); + intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected); + mContext.sendBroadcast(intent); + } + + /** + * Record the detailed state of a network. + * @param state the new @{code DetailedState} + */ + private void setDetailedState(NetworkInfo.DetailedState state) { + Log.d(TAG, "setDetailed state, old =" + + mNetworkInfo.getDetailedState() + " and new state=" + state); + if (state != mNetworkInfo.getDetailedState()) { + mNetworkInfo.setDetailedState(state, null, null); + } + } + + private static String removeDoubleQuotes(String string) { + if (string.length() <= 2) return ""; + return string.substring(1, string.length() - 1); + } + + private static String convertToQuotedString(String string) { + return "\"" + string + "\""; + } + + private static String makeString(BitSet set, String[] strings) { + StringBuffer buf = new StringBuffer(); + int nextSetBit = -1; + + /* Make sure all set bits are in [0, strings.length) to avoid + * going out of bounds on strings. (Shouldn't happen, but...) */ + set = set.get(0, strings.length); + + while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { + buf.append(strings[nextSetBit].replace('_', '-')).append(' '); + } + + // remove trailing space + if (set.cardinality() > 0) { + buf.setLength(buf.length() - 1); + } + + return buf.toString(); + } + + private static int lookupString(String string, String[] strings) { + int size = strings.length; + + string = string.replace('-', '_'); + + for (int i = 0; i < size; i++) + if (string.equals(strings[i])) + return i; + + // if we ever get here, we should probably add the + // value to WifiConfiguration to reflect that it's + // supported by the WPA supplicant + Log.w(TAG, "Failed to look-up a string: " + string); + + return -1; + } + + private int addOrUpdateNetworkNative(WifiConfiguration config) { + /* + * If the supplied networkId is -1, we create a new empty + * network configuration. Otherwise, the networkId should + * refer to an existing configuration. + */ + int netId = config.networkId; + boolean newNetwork = netId == -1; + // networkId of -1 means we want to create a new network + + if (newNetwork) { + netId = WifiNative.addNetworkCommand(); + if (netId < 0) { + Log.e(TAG, "Failed to add a network!"); + return -1; + } + } + + setVariables: { + + if (config.SSID != null && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.ssidVarName, + config.SSID)) { + Log.d(TAG, "failed to set SSID: "+config.SSID); + break setVariables; + } + + if (config.BSSID != null && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.bssidVarName, + config.BSSID)) { + Log.d(TAG, "failed to set BSSID: "+config.BSSID); + break setVariables; + } + + String allowedKeyManagementString = + makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); + if (config.allowedKeyManagement.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.KeyMgmt.varName, + allowedKeyManagementString)) { + Log.d(TAG, "failed to set key_mgmt: "+ + allowedKeyManagementString); + break setVariables; + } + + String allowedProtocolsString = + makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); + if (config.allowedProtocols.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.Protocol.varName, + allowedProtocolsString)) { + Log.d(TAG, "failed to set proto: "+ + allowedProtocolsString); + break setVariables; + } + + String allowedAuthAlgorithmsString = + makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); + if (config.allowedAuthAlgorithms.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.AuthAlgorithm.varName, + allowedAuthAlgorithmsString)) { + Log.d(TAG, "failed to set auth_alg: "+ + allowedAuthAlgorithmsString); + break setVariables; + } + + String allowedPairwiseCiphersString = + makeString(config.allowedPairwiseCiphers, + WifiConfiguration.PairwiseCipher.strings); + if (config.allowedPairwiseCiphers.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.PairwiseCipher.varName, + allowedPairwiseCiphersString)) { + Log.d(TAG, "failed to set pairwise: "+ + allowedPairwiseCiphersString); + break setVariables; + } + + String allowedGroupCiphersString = + makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); + if (config.allowedGroupCiphers.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.GroupCipher.varName, + allowedGroupCiphersString)) { + Log.d(TAG, "failed to set group: "+ + allowedGroupCiphersString); + break setVariables; + } + + // Prevent client screw-up by passing in a WifiConfiguration we gave it + // by preventing "*" as a key. + if (config.preSharedKey != null && !config.preSharedKey.equals("*") && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.pskVarName, + config.preSharedKey)) { + Log.d(TAG, "failed to set psk: "+config.preSharedKey); + break setVariables; + } + + boolean hasSetKey = false; + if (config.wepKeys != null) { + for (int i = 0; i < config.wepKeys.length; i++) { + // Prevent client screw-up by passing in a WifiConfiguration we gave it + // by preventing "*" as a key. + if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.wepKeyVarNames[i], + config.wepKeys[i])) { + Log.d(TAG, + "failed to set wep_key"+i+": " + + config.wepKeys[i]); + break setVariables; + } + hasSetKey = true; + } + } + } + + if (hasSetKey) { + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.wepTxKeyIdxVarName, + Integer.toString(config.wepTxKeyIndex))) { + Log.d(TAG, + "failed to set wep_tx_keyidx: "+ + config.wepTxKeyIndex); + break setVariables; + } + } + + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.priorityVarName, + Integer.toString(config.priority))) { + Log.d(TAG, config.SSID + ": failed to set priority: " + +config.priority); + break setVariables; + } + + if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.hiddenSSIDVarName, + Integer.toString(config.hiddenSSID ? 1 : 0))) { + Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ + config.hiddenSSID); + break setVariables; + } + + for (WifiConfiguration.EnterpriseField field + : config.enterpriseFields) { + String varName = field.varName(); + String value = field.value(); + if (value != null) { + if (field != config.eap) { + value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); + } + if (!WifiNative.setNetworkVariableCommand( + netId, + varName, + value)) { + Log.d(TAG, config.SSID + ": failed to set " + varName + + ": " + value); + break setVariables; + } + } + } + return netId; + } + + if (newNetwork) { + WifiNative.removeNetworkCommand(netId); + Log.d(TAG, + "Failed to set a network variable, removed network: " + + netId); + } + + return -1; + } + + private List<WifiConfiguration> getConfiguredNetworksNative() { + String listStr = WifiNative.listNetworksCommand(); + + List<WifiConfiguration> networks = + new ArrayList<WifiConfiguration>(); + if (listStr == null) + return networks; + + String[] lines = listStr.split("\n"); + // Skip the first line, which is a header + for (int i = 1; i < lines.length; i++) { + String[] result = lines[i].split("\t"); + // network-id | ssid | bssid | flags + WifiConfiguration config = new WifiConfiguration(); + try { + config.networkId = Integer.parseInt(result[0]); + } catch(NumberFormatException e) { + continue; + } + if (result.length > 3) { + if (result[3].indexOf("[CURRENT]") != -1) + config.status = WifiConfiguration.Status.CURRENT; + else if (result[3].indexOf("[DISABLED]") != -1) + config.status = WifiConfiguration.Status.DISABLED; + else + config.status = WifiConfiguration.Status.ENABLED; + } else { + config.status = WifiConfiguration.Status.ENABLED; + } + readNetworkVariables(config); + networks.add(config); + } + return networks; + } + + /** + * Read the variables from the supplicant daemon that are needed to + * fill in the WifiConfiguration object. + * + * @param config the {@link WifiConfiguration} object to be filled in. + */ + private void readNetworkVariables(WifiConfiguration config) { + + int netId = config.networkId; + if (netId < 0) + return; + + /* + * TODO: maybe should have a native method that takes an array of + * variable names and returns an array of values. But we'd still + * be doing a round trip to the supplicant daemon for each variable. + */ + String value; + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); + if (!TextUtils.isEmpty(value)) { + config.SSID = removeDoubleQuotes(value); + } else { + config.SSID = null; + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); + if (!TextUtils.isEmpty(value)) { + config.BSSID = value; + } else { + config.BSSID = null; + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); + config.priority = -1; + if (!TextUtils.isEmpty(value)) { + try { + config.priority = Integer.parseInt(value); + } catch (NumberFormatException ignore) { + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); + config.hiddenSSID = false; + if (!TextUtils.isEmpty(value)) { + try { + config.hiddenSSID = Integer.parseInt(value) != 0; + } catch (NumberFormatException ignore) { + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); + config.wepTxKeyIndex = -1; + if (!TextUtils.isEmpty(value)) { + try { + config.wepTxKeyIndex = Integer.parseInt(value); + } catch (NumberFormatException ignore) { + } + } + + for (int i = 0; i < 4; i++) { + value = WifiNative.getNetworkVariableCommand(netId, + WifiConfiguration.wepKeyVarNames[i]); + if (!TextUtils.isEmpty(value)) { + config.wepKeys[i] = value; + } else { + config.wepKeys[i] = null; + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); + if (!TextUtils.isEmpty(value)) { + config.preSharedKey = value; + } else { + config.preSharedKey = null; + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.Protocol.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.Protocol.strings); + if (0 <= index) { + config.allowedProtocols.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.KeyMgmt.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.KeyMgmt.strings); + if (0 <= index) { + config.allowedKeyManagement.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.AuthAlgorithm.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.AuthAlgorithm.strings); + if (0 <= index) { + config.allowedAuthAlgorithms.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.PairwiseCipher.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.PairwiseCipher.strings); + if (0 <= index) { + config.allowedPairwiseCiphers.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.GroupCipher.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.GroupCipher.strings); + if (0 <= index) { + config.allowedGroupCiphers.set(index); + } + } + } + + for (WifiConfiguration.EnterpriseField field : + config.enterpriseFields) { + value = WifiNative.getNetworkVariableCommand(netId, + field.varName()); + if (!TextUtils.isEmpty(value)) { + if (field != config.eap) value = removeDoubleQuotes(value); + field.setValue(value); + } + } + + } + + /** + * Poll for info not reported via events + * RSSI & Linkspeed + */ + private void requestPolledInfo() { + int newRssi = WifiNative.getRssiCommand(); + if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values + /* some implementations avoid negative values by adding 256 + * so we need to adjust for that here. + */ + if (newRssi > 0) newRssi -= 256; + mWifiInfo.setRssi(newRssi); + /* + * Rather then sending the raw RSSI out every time it + * changes, we precalculate the signal level that would + * be displayed in the status bar, and only send the + * broadcast if that much more coarse-grained number + * changes. This cuts down greatly on the number of + * broadcasts, at the cost of not mWifiInforming others + * interested in RSSI of all the changes in signal + * level. + */ + // TODO: The second arg to the call below needs to be a symbol somewhere, but + // it's actually the size of an array of icons that's private + // to StatusBar Policy. + int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); + if (newSignalLevel != mLastSignalLevel) { + sendRssiChangeBroadcast(newRssi); + } + mLastSignalLevel = newSignalLevel; + } else { + mWifiInfo.setRssi(-200); + } + int newLinkSpeed = WifiNative.getLinkSpeedCommand(); + if (newLinkSpeed != -1) { + mWifiInfo.setLinkSpeed(newLinkSpeed); + } + } + + /** + * Resets the Wi-Fi Connections by clearing any state, resetting any sockets + * using the interface, stopping DHCP & disabling interface + */ + private void handleNetworkDisconnect() { + Log.d(TAG, "Reset connections and stopping DHCP"); + + /* + * Reset connections & stop DHCP + */ + NetworkUtils.resetConnections(mInterfaceName); + + if (!NetworkUtils.stopDhcp(mInterfaceName)) { + Log.e(TAG, "Could not stop DHCP"); + } + + /* Disable interface */ + NetworkUtils.disableInterface(mInterfaceName); + + /* send event to CM & network change broadcast */ + setDetailedState(DetailedState.DISCONNECTED); + sendNetworkStateChangeBroadcast(mLastBssid); + + /* Reset data structures */ + mWifiInfo.setIpAddress(0); + mWifiInfo.setBSSID(null); + mWifiInfo.setSSID(null); + mWifiInfo.setNetworkId(-1); + + /* Clear network properties */ + mNetworkProperties.clear(); + + mLastBssid= null; + mLastNetworkId = -1; + + } + + + /********************************************************* + * Notifications from WifiMonitor + ********************************************************/ + + /** + * A structure for supplying information about a supplicant state + * change in the STATE_CHANGE event message that comes from the + * WifiMonitor + * thread. + */ + private static class StateChangeResult { + StateChangeResult(int networkId, String BSSID, Object state) { + this.state = state; + this.BSSID = BSSID; + this.networkId = networkId; + } + int networkId; + String BSSID; + Object state; + } + + /** + * Send the tracker a notification that a user-entered password key + * may be incorrect (i.e., caused authentication to fail). + */ + void notifyPasswordKeyMayBeIncorrect() { + sendMessage(PASSWORD_MAY_BE_INCORRECT_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 a connection to the supplicant + * daemon has been established. + */ + 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, + new StateChangeResult(networkId, BSSID, newState))); + } else { + sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, + new StateChangeResult(networkId, BSSID, newState))); + } + } + + /** + * 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 notifyDriverStarted() { + sendMessage(DRIVER_START_EVENT); + } + + void notifyDriverStopped() { + sendMessage(DRIVER_STOP_EVENT); + } + + void notifyDriverHung() { + setWifiEnabled(false); + setWifiEnabled(true); + } + + + /******************************************************** + * HSM states + *******************************************************/ + + class DefaultState extends HierarchicalState { + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch (message.what) { + /* Synchronous call returns */ + case CMD_PING_SUPPLICANT: + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_RECONNECT: + case CMD_REASSOCIATE: + case CMD_REMOVE_NETWORK: + case CMD_ENABLE_NETWORK: + case CMD_DISABLE_NETWORK: + case CMD_ADD_OR_UPDATE_NETWORK: + case CMD_GET_RSSI: + case CMD_GET_RSSI_APPROX: + case CMD_GET_LINK_SPEED: + case CMD_GET_MAC_ADDR: + case CMD_SAVE_CONFIG: + case CMD_CONNECTION_STATUS: + case CMD_GET_NETWORK_CONFIG: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = false; + syncParams.mSyncReturn.intValue = -1; + syncParams.mSyncReturn.stringValue = null; + syncParams.mSyncReturn.configList = null; + notifyOnMsgObject(message); + } + break; + case CMD_ENABLE_RSSI_POLL: + mEnableRssiPolling = (message.arg1 == 1); + mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL); + break; + /* Discard */ + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_RECONFIGURE_IP: + case SUP_CONNECTION_EVENT: + case SUP_DISCONNECTION_EVENT: + case DRIVER_START_EVENT: + case DRIVER_STOP_EVENT: + case NETWORK_CONNECTION_EVENT: + case NETWORK_DISCONNECTION_EVENT: + case SCAN_RESULTS_EVENT: + case SUPPLICANT_STATE_CHANGE_EVENT: + case PASSWORD_MAY_BE_INCORRECT_EVENT: + case CMD_BLACKLIST_NETWORK: + case CMD_CLEAR_BLACKLIST: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_REQUEST_CM_WAKELOCK: + break; + default: + Log.e(TAG, "Error! unhandled message" + message); + break; + } + return HANDLED; + } + } + + class InitialState extends HierarchicalState { + @Override + //TODO: could move logging into a common class + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + // [31-8] Reserved for future use + // [7 - 0] HSM state change + // 50021 wifi_state_changed (custom|1|5) + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + if (WifiNative.isDriverLoaded()) { + transitionTo(mDriverLoadedState); + } + else { + transitionTo(mDriverUnloadedState); + } + } + } + + class DriverLoadingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + final Message message = new Message(); + message.copyFrom(getCurrentMessage()); + /* TODO: add a timeout to fail when driver load is hung. + * Similarly for driver unload. + */ + new Thread(new Runnable() { + public void run() { + sWakeLock.acquire(); + //enabling state + switch(message.arg1) { + case WIFI_STATE_ENABLING: + setWifiState(WIFI_STATE_ENABLING); + break; + case WIFI_AP_STATE_ENABLING: + setWifiApState(WIFI_AP_STATE_ENABLING); + break; + } + + if(WifiNative.loadDriver()) { + Log.d(TAG, "Driver load successful"); + sendMessage(CMD_LOAD_DRIVER_SUCCESS); + } else { + Log.e(TAG, "Failed to load driver!"); + switch(message.arg1) { + case WIFI_STATE_ENABLING: + setWifiState(WIFI_STATE_UNKNOWN); + break; + case WIFI_AP_STATE_ENABLING: + setWifiApState(WIFI_AP_STATE_FAILED); + break; + } + sendMessage(CMD_LOAD_DRIVER_FAILURE); + } + sWakeLock.release(); + } + }).start(); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_LOAD_DRIVER_SUCCESS: + transitionTo(mDriverLoadedState); + break; + case CMD_LOAD_DRIVER_FAILURE: + transitionTo(mDriverFailedState); + 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_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + 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 DriverLoadedState extends HierarchicalState { + @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_UNLOAD_DRIVER: + transitionTo(mDriverUnloadingState); + break; + case CMD_START_SUPPLICANT: + if(WifiNative.startSupplicant()) { + Log.d(TAG, "Supplicant start successful"); + mWifiMonitor.startMonitoring(); + setWifiState(WIFI_STATE_ENABLED); + transitionTo(mWaitForSupState); + } else { + Log.e(TAG, "Failed to start supplicant!"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); + } + break; + case CMD_START_AP: + try { + nwService.startAccessPoint((WifiConfiguration) message.obj, + mInterfaceName, + SOFTAP_IFACE); + } catch(Exception e) { + Log.e(TAG, "Exception in startAccessPoint()"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); + break; + } + Log.d(TAG, "Soft AP start successful"); + setWifiApState(WIFI_AP_STATE_ENABLED); + transitionTo(mSoftApStartedState); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverUnloadingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + final Message message = new Message(); + message.copyFrom(getCurrentMessage()); + new Thread(new Runnable() { + public void run() { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + sWakeLock.acquire(); + if(WifiNative.unloadDriver()) { + Log.d(TAG, "Driver unload successful"); + sendMessage(CMD_UNLOAD_DRIVER_SUCCESS); + + switch(message.arg1) { + case WIFI_STATE_DISABLED: + case WIFI_STATE_UNKNOWN: + setWifiState(message.arg1); + break; + case WIFI_AP_STATE_DISABLED: + case WIFI_AP_STATE_FAILED: + setWifiApState(message.arg1); + break; + } + } else { + Log.e(TAG, "Failed to unload driver!"); + sendMessage(CMD_UNLOAD_DRIVER_FAILURE); + + switch(message.arg1) { + case WIFI_STATE_DISABLED: + case WIFI_STATE_UNKNOWN: + setWifiState(WIFI_STATE_UNKNOWN); + break; + case WIFI_AP_STATE_DISABLED: + case WIFI_AP_STATE_FAILED: + setWifiApState(WIFI_AP_STATE_FAILED); + break; + } + } + sWakeLock.release(); + } + }).start(); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_UNLOAD_DRIVER_SUCCESS: + transitionTo(mDriverUnloadedState); + break; + case CMD_UNLOAD_DRIVER_FAILURE: + transitionTo(mDriverFailedState); + 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_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + 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 DriverUnloadedState extends HierarchicalState { + @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_LOAD_DRIVER: + transitionTo(mDriverLoadingState); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverFailedState extends HierarchicalState { + @Override + public void enter() { + Log.e(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"); + return NOT_HANDLED; + } + } + + + class WaitForSupState extends HierarchicalState { + @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 SUP_CONNECTION_EVENT: + Log.d(TAG, "Supplicant connection established"); + mSupplicantStateTracker.resetSupplicantState(); + /* Initialize data structures */ + mLastBssid = null; + mLastNetworkId = -1; + mLastSignalLevel = -1; + + mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand()); + + //TODO: initialize and fix multicast filtering + //mWM.initializeMulticastFiltering(); + + if (mBluetoothA2dp == null) { + mBluetoothA2dp = new BluetoothA2dp(mContext); + } + checkIsBluetoothPlaying(); + + checkUseStaticIp(); + sendSupplicantConnectionChangedBroadcast(true); + transitionTo(mDriverSupReadyState); + break; + case CMD_STOP_SUPPLICANT: + Log.d(TAG, "Stop supplicant received"); + WifiNative.stopSupplicant(); + transitionTo(mDriverLoadedState); + break; + /* Fail soft ap when waiting for supplicant start */ + case CMD_START_AP: + Log.d(TAG, "Failed to start soft AP with a running supplicant"); + setWifiApState(WIFI_AP_STATE_FAILED); + break; + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + case CMD_STOP_AP: + case CMD_START_SUPPLICANT: + case CMD_UNLOAD_DRIVER: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverSupReadyState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + /* Initialize for connect mode operation at start */ + mIsScanMode = false; + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch(message.what) { + case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ + Log.d(TAG, "Stop supplicant received"); + WifiNative.stopSupplicant(); + //$FALL-THROUGH$ + case SUP_DISCONNECTION_EVENT: /* Supplicant died */ + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + WifiNative.closeSupplicantConnection(); + handleNetworkDisconnect(); + sendSupplicantConnectionChangedBroadcast(false); + mSupplicantStateTracker.resetSupplicantState(); + transitionTo(mDriverLoadedState); + + /* When supplicant dies, unload driver and enter failed state */ + //TODO: consider bringing up supplicant again + if (message.what == SUP_DISCONNECTION_EVENT) { + Log.d(TAG, "Supplicant died, unloading driver"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); + } + break; + case CMD_START_DRIVER: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + WifiNative.startDriverCommand(); + transitionTo(mDriverStartingState); + break; + case SCAN_RESULTS_EVENT: + setScanResults(WifiNative.scanResultsCommand()); + sendScanResultsAvailableBroadcast(); + break; + case CMD_PING_SUPPLICANT: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.pingCommand(); + notifyOnMsgObject(message); + break; + case CMD_ADD_OR_UPDATE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + syncParams = (SyncParams) message.obj; + WifiConfiguration config = (WifiConfiguration) syncParams.mParameter; + syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config); + notifyOnMsgObject(message); + break; + case CMD_REMOVE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand( + message.arg1); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.removeNetworkCommand(message.arg1); + } + break; + case CMD_ENABLE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter; + syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand( + enableNetParams.netId, enableNetParams.disableOthers); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1); + } + break; + case CMD_DISABLE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand( + message.arg1); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disableNetworkCommand(message.arg1); + } + break; + case CMD_BLACKLIST_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + WifiNative.addToBlacklistCommand((String)message.obj); + break; + case CMD_CLEAR_BLACKLIST: + WifiNative.clearBlacklistCommand(); + break; + case CMD_GET_NETWORK_CONFIG: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.configList = getConfiguredNetworksNative(); + notifyOnMsgObject(message); + break; + case CMD_SAVE_CONFIG: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.saveConfigCommand(); + } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (Exception e) { + // Try again later + } + } + break; + case CMD_CONNECTION_STATUS: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.stringValue = WifiNative.statusCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_MAC_ADDR: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand(); + notifyOnMsgObject(message); + break; + /* Cannot start soft AP while in client mode */ + case CMD_START_AP: + Log.d(TAG, "Failed to start soft AP with a running supplicant"); + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + setWifiApState(WIFI_AP_STATE_FAILED); + break; + case CMD_SET_SCAN_MODE: + mIsScanMode = (message.arg1 == SCAN_ONLY_MODE); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class DriverStartingState extends HierarchicalState { + @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 DRIVER_START_EVENT: + transitionTo(mDriverStartedState); + break; + /* Queue driver commands & connection events */ + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case SUPPLICANT_STATE_CHANGE_EVENT: + case NETWORK_CONNECTION_EVENT: + case NETWORK_DISCONNECTION_EVENT: + case PASSWORD_MAY_BE_INCORRECT_EVENT: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + /* Queue the asynchronous version of these commands */ + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_REASSOCIATE: + case CMD_RECONNECT: + if (message.arg2 != SYNCHRONOUS_CALL) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverStartedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + try { + mBatteryStats.noteWifiRunning(); + } catch (RemoteException ignore) {} + + /* Initialize channel count */ + setNumAllowedChannels(); + + if (mIsScanMode) { + WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); + WifiNative.disconnectCommand(); + transitionTo(mScanModeState); + } else { + WifiNative.setScanResultHandlingCommand(CONNECT_MODE); + /* If supplicant has already connected, before we could finish establishing + * the control channel connection, we miss all the supplicant events. + * Disconnect and reconnect when driver has started to ensure we receive + * all supplicant events. + * + * TODO: This is a bit unclean, ideally the supplicant should never + * connect until told to do so by the framework + */ + WifiNative.disconnectCommand(); + WifiNative.reconnectCommand(); + transitionTo(mConnectModeState); + } + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch(message.what) { + case CMD_SET_SCAN_TYPE: + if (message.arg1 == SCAN_ACTIVE) { + WifiNative.setScanModeCommand(true); + } else { + WifiNative.setScanModeCommand(false); + } + break; + case CMD_SET_POWER_MODE: + WifiNative.setPowerModeCommand(message.arg1); + break; + case CMD_SET_BLUETOOTH_COEXISTENCE: + WifiNative.setBluetoothCoexistenceModeCommand(message.arg1); + break; + case CMD_SET_BLUETOOTH_SCAN_MODE: + WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1); + break; + case CMD_SET_NUM_ALLOWED_CHANNELS: + mNumAllowedChannels = message.arg1; + WifiNative.setNumAllowedChannelsCommand(message.arg1); + break; + case CMD_START_DRIVER: + /* Ignore another driver start */ + break; + case CMD_STOP_DRIVER: + WifiNative.stopDriverCommand(); + transitionTo(mDriverStoppingState); + break; + case CMD_REQUEST_CM_WAKELOCK: + if (mCm == null) { + mCm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + } + mCm.requestNetworkTransitionWakelock(TAG); + break; + case CMD_START_PACKET_FILTERING: + WifiNative.startPacketFiltering(); + break; + case CMD_STOP_PACKET_FILTERING: + WifiNative.stopPacketFiltering(); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + @Override + public void exit() { + if (DBG) Log.d(TAG, getName() + "\n"); + try { + mBatteryStats.noteWifiStopped(); + } catch (RemoteException ignore) { } + } + } + + class DriverStoppingState extends HierarchicalState { + @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 DRIVER_STOP_EVENT: + transitionTo(mDriverStoppedState); + break; + /* Queue driver commands */ + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + /* Queue the asynchronous version of these commands */ + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_REASSOCIATE: + case CMD_RECONNECT: + if (message.arg2 != SYNCHRONOUS_CALL) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverStoppedState extends HierarchicalState { + @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"); + return NOT_HANDLED; + } + } + + class ScanModeState extends HierarchicalState { + @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"); + SyncParams syncParams; + switch(message.what) { + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + /* Ignore */ + return HANDLED; + } else { + WifiNative.setScanResultHandlingCommand(message.arg1); + WifiNative.reconnectCommand(); + mIsScanMode = false; + transitionTo(mDisconnectedState); + } + break; + case CMD_START_SCAN: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.scanCommand( + message.arg1 == SCAN_ACTIVE); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + } + break; + /* Ignore */ + case CMD_DISCONNECT: + case CMD_RECONNECT: + case CMD_REASSOCIATE: + case SUPPLICANT_STATE_CHANGE_EVENT: + case NETWORK_CONNECTION_EVENT: + case NETWORK_DISCONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class ConnectModeState extends HierarchicalState { + @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"); + SyncParams syncParams; + StateChangeResult stateChangeResult; + switch(message.what) { + case PASSWORD_MAY_BE_INCORRECT_EVENT: + mPasswordKeyMayBeIncorrect = true; + break; + case SUPPLICANT_STATE_CHANGE_EVENT: + stateChangeResult = (StateChangeResult) message.obj; + mSupplicantStateTracker.handleEvent(stateChangeResult); + break; + case CMD_START_SCAN: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = true; + notifyOnMsgObject(message); + } + /* We need to set scan type in completed state */ + Message newMsg = obtainMessage(); + newMsg.copyFrom(message); + mSupplicantStateTracker.sendMessage(newMsg); + break; + /* Do a redundant disconnect without transition */ + case CMD_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + break; + case CMD_RECONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.reconnectCommand(); + } + break; + case CMD_REASSOCIATE: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.reassociateCommand(); + } + break; + case 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: + Log.d(TAG,"Network connection established"); + stateChangeResult = (StateChangeResult) message.obj; + + mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID); + mWifiInfo.setNetworkId(stateChangeResult.networkId); + mLastNetworkId = stateChangeResult.networkId; + + /* send event to CM & network change broadcast */ + setDetailedState(DetailedState.OBTAINING_IPADDR); + sendNetworkStateChangeBroadcast(mLastBssid); + + transitionTo(mConnectingState); + break; + case NETWORK_DISCONNECTION_EVENT: + Log.d(TAG,"Network connection lost"); + handleNetworkDisconnect(); + transitionTo(mDisconnectedState); + break; + case CMD_GET_RSSI: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_RSSI_APPROX: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_LINK_SPEED: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand(); + notifyOnMsgObject(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class ConnectingState extends HierarchicalState { + boolean modifiedBluetoothCoexistenceMode; + int powerMode; + Thread mDhcpThread; + + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + if (!mUseStaticIp) { + + mDhcpThread = null; + modifiedBluetoothCoexistenceMode = false; + powerMode = DRIVER_POWER_MODE_AUTO; + + if (shouldDisableCoexistenceMode()) { + /* + * There are problems setting the Wi-Fi driver's power + * mode to active when bluetooth coexistence mode is + * enabled or sense. + * <p> + * We set Wi-Fi to active mode when + * obtaining an IP address because we've found + * compatibility issues with some routers with low power + * mode. + * <p> + * In order for this active power mode to properly be set, + * we disable coexistence mode until we're done with + * obtaining an IP address. One exception is if we + * are currently connected to a headset, since disabling + * coexistence would interrupt that connection. + */ + modifiedBluetoothCoexistenceMode = true; + + // Disable the coexistence mode + WifiNative.setBluetoothCoexistenceModeCommand( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); + } + + powerMode = WifiNative.getPowerModeCommand(); + if (powerMode < 0) { + // Handle the case where supplicant driver does not support + // getPowerModeCommand. + powerMode = DRIVER_POWER_MODE_AUTO; + } + if (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); + } + + Log.d(TAG, "DHCP request started"); + mDhcpThread = new Thread(new Runnable() { + public void run() { + if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { + Log.d(TAG, "DHCP request succeeded"); + sendMessage(CMD_IP_CONFIG_SUCCESS); + } else { + Log.d(TAG, "DHCP request failed: " + + NetworkUtils.getDhcpError()); + sendMessage(CMD_IP_CONFIG_FAILURE); + } + } + }); + mDhcpThread.start(); + } else { + if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { + Log.v(TAG, "Static IP configuration succeeded"); + sendMessage(CMD_IP_CONFIG_SUCCESS); + } else { + Log.v(TAG, "Static IP configuration failed"); + sendMessage(CMD_IP_CONFIG_FAILURE); + } + } + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + + switch(message.what) { + case CMD_IP_CONFIG_SUCCESS: + mReconnectCount = 0; + mLastSignalLevel = -1; // force update of signal strength + mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); + Log.d(TAG, "IP configuration: " + mDhcpInfo); + configureNetworkProperties(); + setDetailedState(DetailedState.CONNECTED); + sendNetworkStateChangeBroadcast(mLastBssid); + //TODO: we could also detect an IP config change + // from a DHCP renewal and send out a config change + // broadcast + if (mConfigChanged) { + sendConfigChangeBroadcast(); + mConfigChanged = false; + } + transitionTo(mConnectedState); + break; + case CMD_IP_CONFIG_FAILURE: + mWifiInfo.setIpAddress(0); + + Log.e(TAG, "IP configuration failed"); + /** + * If we've exceeded the maximum number of retries for DHCP + * to a given network, disable the network + */ + if (++mReconnectCount > getMaxDhcpRetries()) { + Log.e(TAG, "Failed " + + mReconnectCount + " times, Disabling " + mLastNetworkId); + WifiNative.disableNetworkCommand(mLastNetworkId); + } + + /* DHCP times out after about 30 seconds, we do a + * disconnect and an immediate reconnect to try again + */ + WifiNative.disconnectCommand(); + WifiNative.reconnectCommand(); + transitionTo(mDisconnectingState); + break; + case CMD_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + SyncParams syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + transitionTo(mDisconnectingState); + break; + /* Ignore */ + case NETWORK_CONNECTION_EVENT: + break; + case CMD_STOP_DRIVER: + sendMessage(CMD_DISCONNECT); + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + sendMessage(CMD_DISCONNECT); + deferMessage(message); + } + break; + case CMD_RECONFIGURE_IP: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + + @Override + public void exit() { + /* reset power state & bluetooth coexistence if on DHCP */ + if (!mUseStaticIp) { + if (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(powerMode); + } + + if (modifiedBluetoothCoexistenceMode) { + // Set the coexistence mode back to its default value + WifiNative.setBluetoothCoexistenceModeCommand( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); + } + } + + } + } + + class ConnectedState extends HierarchicalState { + @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_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + SyncParams syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + transitionTo(mDisconnectingState); + break; + case CMD_RECONFIGURE_IP: + Log.d(TAG,"Reconfiguring IP on connection"); + NetworkUtils.resetConnections(mInterfaceName); + transitionTo(mConnectingState); + break; + case CMD_STOP_DRIVER: + sendMessage(CMD_DISCONNECT); + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + sendMessage(CMD_DISCONNECT); + deferMessage(message); + } + break; + /* Ignore */ + case NETWORK_CONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DisconnectingState extends HierarchicalState { + @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_STOP_DRIVER: /* Stop driver only after disconnect handled */ + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DisconnectedState extends HierarchicalState { + @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_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + WifiNative.setScanResultHandlingCommand(message.arg1); + //Supplicant disconnect to prevent further connects + WifiNative.disconnectCommand(); + mIsScanMode = true; + transitionTo(mScanModeState); + } + break; + /* Ignore network disconnect */ + case NETWORK_DISCONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class SoftApStartedState extends HierarchicalState { + @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_STOP_AP: + Log.d(TAG,"Stopping Soft AP"); + setWifiApState(WIFI_AP_STATE_DISABLING); + try { + nwService.stopAccessPoint(); + } catch(Exception e) { + Log.e(TAG, "Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + break; + case CMD_START_AP: + Log.d(TAG,"SoftAP set on a running access point"); + try { + nwService.setAccessPoint((WifiConfiguration) message.obj, + mInterfaceName, + SOFTAP_IFACE); + } catch(Exception e) { + Log.e(TAG, "Exception in nwService during soft AP set"); + try { + nwService.stopAccessPoint(); + } catch (Exception ee) { + Slog.e(TAG, "Could not stop AP, :" + ee); + } + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); + } + break; + /* Fail client mode operation when soft AP is enabled */ + case CMD_START_SUPPLICANT: + Log.e(TAG,"Cannot start supplicant with a running soft AP"); + setWifiState(WIFI_STATE_UNKNOWN); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + + class SupplicantStateTracker extends HierarchicalStateMachine { + + private int mRssiPollToken = 0; + + /** + * The max number of the WPA supplicant loop iterations before we + * decide that the loop should be terminated: + */ + private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4; + private int mLoopDetectIndex = 0; + private int mLoopDetectCount = 0; + + /** + * Supplicant state change commands follow + * the ordinal values defined in SupplicantState.java + */ + private static final int DISCONNECTED = 0; + private static final int INACTIVE = 1; + private static final int SCANNING = 2; + private static final int ASSOCIATING = 3; + private static final int ASSOCIATED = 4; + private static final int FOUR_WAY_HANDSHAKE = 5; + private static final int GROUP_HANDSHAKE = 6; + private static final int COMPLETED = 7; + private static final int DORMANT = 8; + private static final int UNINITIALIZED = 9; + private static final int INVALID = 10; + + private HierarchicalState mUninitializedState = new UninitializedState(); + private HierarchicalState mInitializedState = new InitializedState();; + private HierarchicalState mInactiveState = new InactiveState(); + private HierarchicalState mDisconnectState = new DisconnectedState(); + private HierarchicalState mScanState = new ScanState(); + private HierarchicalState mConnectState = new ConnectState(); + private HierarchicalState mHandshakeState = new HandshakeState(); + private HierarchicalState mCompletedState = new CompletedState(); + private HierarchicalState mDormantState = new DormantState(); + + public SupplicantStateTracker(Context context, Handler target) { + super(TAG, target.getLooper()); + + addState(mUninitializedState); + addState(mInitializedState); + addState(mInactiveState, mInitializedState); + addState(mDisconnectState, mInitializedState); + addState(mScanState, mInitializedState); + addState(mConnectState, mInitializedState); + addState(mHandshakeState, mConnectState); + addState(mCompletedState, mConnectState); + addState(mDormantState, mInitializedState); + + setInitialState(mUninitializedState); + + //start the state machine + start(); + } + + public void handleEvent(StateChangeResult stateChangeResult) { + SupplicantState newState = (SupplicantState) stateChangeResult.state; + + // Supplicant state change + // [31-13] Reserved for future use + // [8 - 0] Supplicant state (as defined in SupplicantState.java) + // 50023 supplicant_state_changed (custom|1|5) + EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal()); + + sendMessage(obtainMessage(newState.ordinal(), stateChangeResult)); + } + + public void resetSupplicantState() { + transitionTo(mUninitializedState); + } + + private void resetLoopDetection() { + mLoopDetectCount = 0; + mLoopDetectIndex = 0; + } + + private boolean handleTransition(Message msg) { + if (DBG) Log.d(TAG, getName() + msg.toString() + "\n"); + switch (msg.what) { + case DISCONNECTED: + transitionTo(mDisconnectState); + break; + case SCANNING: + transitionTo(mScanState); + break; + case ASSOCIATING: + StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; + /* BSSID is valid only in ASSOCIATING state */ + mWifiInfo.setBSSID(stateChangeResult.BSSID); + //$FALL-THROUGH$ + case ASSOCIATED: + case FOUR_WAY_HANDSHAKE: + case GROUP_HANDSHAKE: + transitionTo(mHandshakeState); + break; + case COMPLETED: + transitionTo(mCompletedState); + break; + case DORMANT: + transitionTo(mDormantState); + break; + case INACTIVE: + transitionTo(mInactiveState); + break; + case UNINITIALIZED: + case INVALID: + transitionTo(mUninitializedState); + break; + default: + return NOT_HANDLED; + } + StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; + SupplicantState supState = (SupplicantState) stateChangeResult.state; + setDetailedState(WifiInfo.getDetailedStateOf(supState)); + mWifiInfo.setSupplicantState(supState); + mWifiInfo.setNetworkId(stateChangeResult.networkId); + //TODO: Modify WifiMonitor to report SSID on events + //mWifiInfo.setSSID() + return HANDLED; + } + + /******************************************************** + * HSM states + *******************************************************/ + + class InitializedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_START_SCAN: + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + break; + default: + if (DBG) Log.w(TAG, "Ignoring " + message); + break; + } + return HANDLED; + } + } + + class UninitializedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + mNetworkInfo.setIsAvailable(false); + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + } + @Override + public boolean processMessage(Message message) { + switch(message.what) { + default: + if (!handleTransition(message)) { + if (DBG) Log.w(TAG, "Ignoring " + message); + } + break; + } + return HANDLED; + } + @Override + public void exit() { + mNetworkInfo.setIsAvailable(true); + } + } + + class InactiveState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(false); + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + @Override + public void exit() { + mNetworkInfo.setIsAvailable(true); + } + } + + + class DisconnectedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + resetLoopDetection(); + + /* If a disconnect event happens after a password key failure + * event, disable the network + */ + if (mPasswordKeyMayBeIncorrect) { + Log.d(TAG, "Failed to authenticate, disabling network " + + mWifiInfo.getNetworkId()); + WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId()); + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, true); + } + else { + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class ScanState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mPasswordKeyMayBeIncorrect = false; + resetLoopDetection(); + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class ConnectState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + } + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_START_SCAN: + WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class HandshakeState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + final Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + if (mLoopDetectIndex > message.what) { + mLoopDetectCount++; + } + if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) { + WifiNative.disableNetworkCommand(stateChangeResult.networkId); + mLoopDetectCount = 0; + } + + mLoopDetectIndex = message.what; + + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class CompletedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mRssiPollToken++; + if (mEnableRssiPolling) { + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } + + resetLoopDetection(); + + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + switch(message.what) { + case ASSOCIATING: + case ASSOCIATED: + case FOUR_WAY_HANDSHAKE: + case GROUP_HANDSHAKE: + case COMPLETED: + break; + case CMD_RSSI_POLL: + if (message.arg1 == mRssiPollToken) { + // Get Info and continue polling + requestPolledInfo(); + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } else { + // Polling has completed + } + break; + case CMD_ENABLE_RSSI_POLL: + mRssiPollToken++; + if (mEnableRssiPolling) { + // first poll + requestPolledInfo(); + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } + break; + default: + return handleTransition(message); + } + return HANDLED; + } + } + + class DormantState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + + /* TODO: reconnect is now being handled at DHCP failure handling + * If we run into issues with staying in Dormant state, might + * need a reconnect here + */ + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + } +} diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 388beea..8e1b236 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -16,524 +16,59 @@ package android.net.wifi; -import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; -import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; -import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; -/** - * TODO: Add soft AP states as part of WIFI_STATE_XXX - * Retain WIFI_STATE_ENABLING that indicates driver is loading - * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started - * and WIFI_STATE_FAILED for failure - * Deprecate WIFI_STATE_UNKNOWN - * - * Doing this will simplify the logic for sending broadcasts - */ -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; -import android.app.ActivityManagerNative; -import android.net.NetworkInfo; -import android.net.NetworkStateTracker; -import android.net.DhcpInfo; -import android.net.NetworkUtils; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.ConnectivityManager; -import android.net.NetworkInfo.DetailedState; -import android.net.NetworkInfo.State; +import android.net.NetworkInfo; import android.net.NetworkProperties; -import android.os.Binder; -import android.os.Message; -import android.os.Parcelable; +import android.net.NetworkStateTracker; import android.os.Handler; -import android.os.IBinder; -import android.os.INetworkManagementService; -import android.os.PowerManager; -import android.os.SystemProperties; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.Process; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.EventLog; -import android.util.Log; -import android.util.Slog; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.backup.IBackupManager; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothA2dp; -import android.content.ContentResolver; -import android.content.Intent; -import android.content.Context; -import android.database.ContentObserver; -import com.android.internal.app.IBatteryStats; -import com.android.internal.util.HierarchicalState; -import com.android.internal.util.HierarchicalStateMachine; +import android.os.Message; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; /** - * Track the state of Wifi connectivity. All event handling is done here, - * and all changes in connectivity state are initiated here. + * Track the state of wifi for connectivity service. * * @hide */ -//TODO: we still need frequent scanning for the case when -// we issue disconnect but need scan results for open network notification -public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker { +public class WifiStateTracker implements NetworkStateTracker { - private static final String TAG = "WifiStateTracker"; private static final String NETWORKTYPE = "WIFI"; - private static final boolean DBG = false; - - /* TODO: fetch a configurable interface */ - private static final String SOFTAP_IFACE = "wl0.1"; - - private WifiMonitor mWifiMonitor; - private INetworkManagementService nwService; - private ConnectivityManager mCm; - - /* Scan results handling */ - private List<ScanResult> mScanResults; - private static final Pattern scanResultPattern = Pattern.compile("\t+"); - private static final int SCAN_RESULT_CACHE_SIZE = 80; - private final LinkedHashMap<String, ScanResult> mScanResultCache; + private static final String TAG = "WifiStateTracker"; - private String mInterfaceName; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); - private int mNumAllowedChannels = 0; - private int mLastSignalLevel = -1; - private String mLastBssid; - private int mLastNetworkId; - private boolean mEnableRssiPolling = false; - private boolean mPasswordKeyMayBeIncorrect = false; - private boolean mUseStaticIp = false; - private int mReconnectCount = 0; - private boolean mIsScanMode = false; - - /** - * Instance of the bluetooth headset helper. This needs to be created - * early because there is a delay before it actually 'connects', as - * noted by its javadoc. If we check before it is connected, it will be - * in an error state and we will not disable coexistence. - */ - private BluetoothHeadset mBluetoothHeadset; - - private BluetoothA2dp mBluetoothA2dp; - - /** - * Observes the static IP address settings. - */ - private SettingsObserver mSettingsObserver; private NetworkProperties mNetworkProperties; - - - // Variables relating to the 'available networks' notification - /** - * The icon to show in the 'available networks' notification. This will also - * be the ID of the Notification given to the NotificationManager. - */ - private static final int ICON_NETWORKS_AVAILABLE = - com.android.internal.R.drawable.stat_notify_wifi_in_range; - /** - * When a notification is shown, we wait this amount before possibly showing it again. - */ - private final long NOTIFICATION_REPEAT_DELAY_MS; - /** - * Whether the user has set the setting to show the 'available networks' notification. - */ - private boolean mNotificationEnabled; - /** - * Observes the user setting to keep {@link #mNotificationEnabled} in sync. - */ - private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; - /** - * The {@link System#currentTimeMillis()} must be at least this value for us - * to show the notification again. - */ - private long mNotificationRepeatTime; - /** - * The Notification object given to the NotificationManager. - */ - private Notification mNotification; - /** - * Whether the notification is being shown, as set by us. That is, if the - * user cancels the notification, we will not receive the callback so this - * will still be true. We only guarantee if this is false, then the - * notification is not showing. - */ - private boolean mNotificationShown; - /** - * The number of continuous scans that must occur before consider the - * supplicant in a scanning state. This allows supplicant to associate with - * remembered networks that are in the scan results. - */ - private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; - /** - * The number of scans since the last network state change. When this - * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the - * supplicant to actually be scanning. When the network state changes to - * something other than scanning, we reset this to 0. - */ - private int mNumScansSinceNetworkStateChange; - - // Held during driver load and unload - private static PowerManager.WakeLock sWakeLock; + private NetworkInfo mNetworkInfo; /* For sending events to connectivity service handler */ private Handler mCsHandler; private Context mContext; - - private DhcpInfo mDhcpInfo; - private WifiInfo mWifiInfo; - private NetworkInfo mNetworkInfo; - private SupplicantStateTracker mSupplicantStateTracker; - - // 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; - private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023; - - /* Load the driver */ - private static final int CMD_LOAD_DRIVER = 1; - /* Unload the driver */ - private static final int CMD_UNLOAD_DRIVER = 2; - /* Indicates driver load succeeded */ - private static final int CMD_LOAD_DRIVER_SUCCESS = 3; - /* Indicates driver load failed */ - private static final int CMD_LOAD_DRIVER_FAILURE = 4; - /* Indicates driver unload succeeded */ - private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5; - /* Indicates driver unload failed */ - private static final int CMD_UNLOAD_DRIVER_FAILURE = 6; - - /* Start the supplicant */ - private static final int CMD_START_SUPPLICANT = 11; - /* Stop the supplicant */ - private static final int CMD_STOP_SUPPLICANT = 12; - /* Start the driver */ - private static final int CMD_START_DRIVER = 13; - /* Start the driver */ - private static final int CMD_STOP_DRIVER = 14; - /* Indicates DHCP succeded */ - private static final int CMD_IP_CONFIG_SUCCESS = 15; - /* Indicates DHCP failed */ - private static final int CMD_IP_CONFIG_FAILURE = 16; - /* Re-configure interface */ - private static final int CMD_RECONFIGURE_IP = 17; - - - /* Start the soft access point */ - private static final int CMD_START_AP = 21; - /* Stop the soft access point */ - private static final int CMD_STOP_AP = 22; - - - /* Supplicant events */ - /* Connection to supplicant established */ - private static final int SUP_CONNECTION_EVENT = 31; - /* Connection to supplicant lost */ - private static final int SUP_DISCONNECTION_EVENT = 32; - /* Driver start completed */ - private static final int DRIVER_START_EVENT = 33; - /* Driver stop completed */ - private static final int DRIVER_STOP_EVENT = 34; - /* Network connection completed */ - private static final int NETWORK_CONNECTION_EVENT = 36; - /* Network disconnection completed */ - private static final int NETWORK_DISCONNECTION_EVENT = 37; - /* Scan results are available */ - private static final int SCAN_RESULTS_EVENT = 38; - /* Supplicate state changed */ - private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39; - /* Password may be incorrect */ - private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40; - - /* Supplicant commands */ - /* Is supplicant alive ? */ - private static final int CMD_PING_SUPPLICANT = 51; - /* Add/update a network configuration */ - private static final int CMD_ADD_OR_UPDATE_NETWORK = 52; - /* Delete a network */ - private static final int CMD_REMOVE_NETWORK = 53; - /* Enable a network. The device will attempt a connection to the given network. */ - private static final int CMD_ENABLE_NETWORK = 54; - /* Disable a network. The device does not attempt a connection to the given network. */ - private static final int CMD_DISABLE_NETWORK = 55; - /* Blacklist network. De-prioritizes the given BSSID for connection. */ - private static final int CMD_BLACKLIST_NETWORK = 56; - /* Clear the blacklist network list */ - private static final int CMD_CLEAR_BLACKLIST = 57; - /* Get the configured networks */ - private static final int CMD_GET_NETWORK_CONFIG = 58; - /* Save configuration */ - private static final int CMD_SAVE_CONFIG = 59; - /* Connection status */ - private static final int CMD_CONNECTION_STATUS = 60; - - /* Supplicant commands after driver start*/ - /* Initiate a scan */ - private static final int CMD_START_SCAN = 71; - /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */ - private static final int CMD_SET_SCAN_MODE = 72; - /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */ - private static final int CMD_SET_SCAN_TYPE = 73; - /* Disconnect from a network */ - private static final int CMD_DISCONNECT = 74; - /* Reconnect to a network */ - private static final int CMD_RECONNECT = 75; - /* Reassociate to a network */ - private static final int CMD_REASSOCIATE = 76; - /* Set power mode - * POWER_MODE_ACTIVE - * POWER_MODE_AUTO - */ - private static final int CMD_SET_POWER_MODE = 77; - /* Set bluetooth co-existence - * BLUETOOTH_COEXISTENCE_MODE_ENABLED - * BLUETOOTH_COEXISTENCE_MODE_DISABLED - * BLUETOOTH_COEXISTENCE_MODE_SENSE - */ - private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78; - /* Enable/disable bluetooth scan mode - * true(1) - * false(0) - */ - private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79; - /* Set number of allowed channels */ - private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80; - /* Request connectivity manager wake lock before driver stop */ - private static final int CMD_REQUEST_CM_WAKELOCK = 81; - /* Enables RSSI poll */ - private static final int CMD_ENABLE_RSSI_POLL = 82; - /* RSSI poll */ - private static final int CMD_RSSI_POLL = 83; - /* Get current RSSI */ - private static final int CMD_GET_RSSI = 84; - /* Get approx current RSSI */ - private static final int CMD_GET_RSSI_APPROX = 85; - /* Get link speed on connection */ - private static final int CMD_GET_LINK_SPEED = 86; - /* Radio mac address */ - private static final int CMD_GET_MAC_ADDR = 87; - /* Set up packet filtering */ - private static final int CMD_START_PACKET_FILTERING = 88; - /* Clear packet filter */ - private static final int CMD_STOP_PACKET_FILTERING = 89; - - /* Connectivity service commands */ - /* Bring down wifi connection */ - private static final int CM_CMD_TEARDOWN = 110; - /* Reconnect to wifi */ - private static final int CM_CMD_RECONNECT = 111; - - /** - * Interval in milliseconds between polling for connection - * status items that are not sent via asynchronous events. - * An example is RSSI (signal strength). - */ - private static final int POLL_RSSI_INTERVAL_MSECS = 3000; - - private static final int CONNECT_MODE = 1; - private static final int SCAN_ONLY_MODE = 2; - - private static final int SCAN_ACTIVE = 1; - private static final int SCAN_PASSIVE = 2; - - /** - * The maximum number of times we will retry a connection to an access point - * for which we have failed in acquiring an IP address from DHCP. A value of - * N means that we will make N+1 connection attempts in all. - * <p> - * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default - * value if a Settings value is not present. - */ - private static final int DEFAULT_MAX_DHCP_RETRIES = 9; - - private static final int DRIVER_POWER_MODE_ACTIVE = 1; - private static final int DRIVER_POWER_MODE_AUTO = 0; - - /* Default parent state */ - private HierarchicalState mDefaultState = new DefaultState(); - /* Temporary initial state */ - private HierarchicalState mInitialState = new InitialState(); - /* Unloading the driver */ - private HierarchicalState mDriverUnloadingState = new DriverUnloadingState(); - /* Loading the driver */ - private HierarchicalState mDriverUnloadedState = new DriverUnloadedState(); - /* Driver load/unload failed */ - private HierarchicalState mDriverFailedState = new DriverFailedState(); - /* Driver loading */ - private HierarchicalState mDriverLoadingState = new DriverLoadingState(); - /* Driver loaded */ - private HierarchicalState mDriverLoadedState = new DriverLoadedState(); - /* Driver loaded, waiting for supplicant to start */ - private HierarchicalState mWaitForSupState = new WaitForSupState(); - - /* Driver loaded and supplicant ready */ - private HierarchicalState mDriverSupReadyState = new DriverSupReadyState(); - /* Driver start issued, waiting for completed event */ - private HierarchicalState mDriverStartingState = new DriverStartingState(); - /* Driver started */ - private HierarchicalState mDriverStartedState = new DriverStartedState(); - /* Driver stopping */ - private HierarchicalState mDriverStoppingState = new DriverStoppingState(); - /* Driver stopped */ - private HierarchicalState mDriverStoppedState = new DriverStoppedState(); - /* Scan for networks, no connection will be established */ - private HierarchicalState mScanModeState = new ScanModeState(); - /* Connecting to an access point */ - private HierarchicalState mConnectModeState = new ConnectModeState(); - /* Fetching IP after network connection (assoc+auth complete) */ - private HierarchicalState mConnectingState = new ConnectingState(); - /* Connected with IP addr */ - private HierarchicalState mConnectedState = new ConnectedState(); - /* disconnect issued, waiting for network disconnect confirmation */ - private HierarchicalState mDisconnectingState = new DisconnectingState(); - /* Network is not connected, supplicant assoc+auth is not complete */ - private HierarchicalState mDisconnectedState = new DisconnectedState(); - - /* Soft Ap is running */ - private HierarchicalState mSoftApStartedState = new SoftApStartedState(); - - /* Argument for Message object to indicate a synchronous call */ - private static final int SYNCHRONOUS_CALL = 1; - private static final int ASYNCHRONOUS_CALL = 0; - - - /** - * One of {@link WifiManager#WIFI_STATE_DISABLED}, - * {@link WifiManager#WIFI_STATE_DISABLING}, - * {@link WifiManager#WIFI_STATE_ENABLED}, - * {@link WifiManager#WIFI_STATE_ENABLING}, - * {@link WifiManager#WIFI_STATE_UNKNOWN} - * - */ - private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED); - - /** - * One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, - * {@link WifiManager#WIFI_AP_STATE_DISABLING}, - * {@link WifiManager#WIFI_AP_STATE_ENABLED}, - * {@link WifiManager#WIFI_AP_STATE_ENABLING}, - * {@link WifiManager#WIFI_AP_STATE_FAILED} - * - */ - private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED); - - private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid()); - private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid()); - - private final IBatteryStats mBatteryStats; + private BroadcastReceiver mWifiStateReceiver; + private WifiManager mWifiManager; public WifiStateTracker(Context context, Handler target) { - super(NETWORKTYPE); - mCsHandler = target; mContext = context; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); - mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); - - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - nwService = INetworkManagementService.Stub.asInterface(b); - - mWifiMonitor = new WifiMonitor(this); - mDhcpInfo = new DhcpInfo(); - mWifiInfo = new WifiInfo(); - mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); - mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler()); - - mBluetoothHeadset = new BluetoothHeadset(mContext, null); mNetworkProperties = new NetworkProperties(); mNetworkInfo.setIsAvailable(false); mNetworkProperties.clear(); - resetNotificationTimer(); setTeardownRequested(false); - mLastBssid = null; - mLastNetworkId = -1; - mLastSignalLevel = -1; - - mScanResultCache = new LinkedHashMap<String, ScanResult>( - SCAN_RESULT_CACHE_SIZE, 0.75f, true) { - /* - * Limit the cache size by SCAN_RESULT_CACHE_SIZE - * elements - */ - @Override - public boolean removeEldestEntry(Map.Entry eldest) { - return SCAN_RESULT_CACHE_SIZE < this.size(); - } - }; - - // Setting is in seconds - NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; - mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target); - mNotificationEnabledSettingObserver.register(); - - mSettingsObserver = new SettingsObserver(new Handler()); - - PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - - addState(mDefaultState); - addState(mInitialState, mDefaultState); - addState(mDriverUnloadingState, mDefaultState); - addState(mDriverUnloadedState, mDefaultState); - addState(mDriverFailedState, mDriverUnloadedState); - addState(mDriverLoadingState, mDefaultState); - addState(mDriverLoadedState, mDefaultState); - addState(mWaitForSupState, mDriverLoadedState); - addState(mDriverSupReadyState, mDefaultState); - addState(mDriverStartingState, mDriverSupReadyState); - addState(mDriverStartedState, mDriverSupReadyState); - addState(mScanModeState, mDriverStartedState); - addState(mConnectModeState, mDriverStartedState); - addState(mConnectingState, mConnectModeState); - addState(mConnectedState, mConnectModeState); - addState(mDisconnectingState, mConnectModeState); - addState(mDisconnectedState, mConnectModeState); - addState(mDriverStoppingState, mDriverSupReadyState); - addState(mDriverStoppedState, mDriverSupReadyState); - addState(mSoftApStartedState, mDefaultState); - - setInitialState(mInitialState); - - if (DBG) setDbg(true); - - //start the state machine - start(); } - /********************************************************* - * NetworkStateTracker interface implementation - ********************************************************/ - public void setTeardownRequested(boolean isRequested) { mTeardownRequested.set(isRequested); } @@ -544,11 +79,17 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ /** * Begin monitoring wifi connectivity - * @deprecated - * - * TODO: remove this from callers */ public void startMonitoring() { + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.CONFIG_CHANGED_ACTION); + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + + mWifiStateReceiver = new WifiStateReceiver(); + mContext.registerReceiver(mWifiStateReceiver, filter); } /** @@ -556,7 +97,8 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ * TODO: do away with return value after making MobileDataStateTracker async */ public boolean teardown() { - sendMessage(CM_CMD_TEARDOWN); + mTeardownRequested.set(true); + mWifiManager.stopWifi(); return true; } @@ -565,7 +107,8 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ * TODO: do away with return value after making MobileDataStateTracker async */ public boolean reconnect() { - sendMessage(CM_CMD_RECONNECT); + mTeardownRequested.set(false); + mWifiManager.startWifi(); return true; } @@ -575,7 +118,7 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ * TODO: do away with return value after making MobileDataStateTracker async */ public boolean setRadio(boolean turnOn) { - setWifiEnabled(turnOn); + mWifiManager.setWifiEnabled(turnOn); return true; } @@ -626,21 +169,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ return -1; } - /* TODO: will go away. - * Notifications are directly handled in WifiStateTracker at checkAndSetNotification() - */ - public void interpretScanResultsAvailable() { - - } - - /** - * Return the name of our WLAN network interface. - * @return the name of our interface. - */ - public String getInterfaceName() { - return mInterfaceName; - } - /** * Check if private DNS route is set for the network */ @@ -698,3279 +226,23 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ return "net.tcp.buffersize.wifi"; } - - /********************************************************* - * Methods exposed for public use - ********************************************************/ - - /** - * TODO: doc - */ - public boolean pingSupplicant() { - return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue; - } - - /** - * TODO: doc - */ - public boolean startScan(boolean forceActive) { - return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ? - SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue; - } - - /** - * TODO: doc - */ - public void setWifiEnabled(boolean enable) { - mLastEnableUid.set(Binder.getCallingUid()); - if (enable) { - /* Argument is the state that is entered prior to load */ - sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)); - sendMessage(CMD_START_SUPPLICANT); - } else { - sendMessage(CMD_STOP_SUPPLICANT); - /* Argument is the state that is entered upon success */ - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0)); - } - } - - /** - * TODO: doc - */ - public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) { - mLastApEnableUid.set(Binder.getCallingUid()); - if (enable) { - /* Argument is the state that is entered prior to load */ - sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0)); - sendMessage(obtainMessage(CMD_START_AP, wifiConfig)); - } else { - sendMessage(CMD_STOP_AP); - /* Argument is the state that is entered upon success */ - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0)); - } - } - - /** - * TODO: doc - */ - public int getWifiState() { - return mWifiState.get(); - } - - /** - * TODO: doc - */ - public String getWifiStateByName() { - switch (mWifiState.get()) { - case WIFI_STATE_DISABLING: - return "disabling"; - case WIFI_STATE_DISABLED: - return "disabled"; - case WIFI_STATE_ENABLING: - return "enabling"; - case WIFI_STATE_ENABLED: - return "enabled"; - case WIFI_STATE_UNKNOWN: - return "unknown state"; - default: - return "[invalid state]"; - } - } - - /** - * TODO: doc - */ - public int getWifiApState() { - return mWifiApState.get(); - } - - /** - * TODO: doc - */ - public String getWifiApStateByName() { - switch (mWifiApState.get()) { - case WIFI_AP_STATE_DISABLING: - return "disabling"; - case WIFI_AP_STATE_DISABLED: - return "disabled"; - case WIFI_AP_STATE_ENABLING: - return "enabling"; - case WIFI_AP_STATE_ENABLED: - return "enabled"; - case WIFI_AP_STATE_FAILED: - return "failed"; - default: - return "[invalid state]"; - } - } - - /** - * Get status information for the current connection, if any. - * @return a {@link WifiInfo} object containing information about the current connection - * - */ - public WifiInfo requestConnectionInfo() { - return mWifiInfo; - } - - public DhcpInfo getDhcpInfo() { - return mDhcpInfo; - } - - /** - * TODO: doc - */ - public void startWifi(boolean enable) { - if (enable) { - sendMessage(CMD_START_DRIVER); - } else { - sendMessage(CMD_STOP_DRIVER); - } - } - - /** - * TODO: doc - */ - public void disconnectAndStop() { - sendMessage(CMD_DISCONNECT); - sendMessage(CMD_STOP_DRIVER); - } - - /** - * TODO: doc - */ - public void setScanOnlyMode(boolean enable) { - if (enable) { - sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0)); - } else { - sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0)); - } - } - - /** - * TODO: doc - */ - public void setScanType(boolean active) { - if (active) { - sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0)); - } else { - sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0)); - } - } - - /** - * TODO: doc - */ - public List<ScanResult> getScanResultsList() { - return mScanResults; - } - - /** - * Disconnect from Access Point - */ - public boolean disconnectCommand() { - return sendSyncMessage(CMD_DISCONNECT).boolValue; - } - - /** - * Initiate a reconnection to AP - */ - public boolean reconnectCommand() { - return sendSyncMessage(CMD_RECONNECT).boolValue; - } - - /** - * Initiate a re-association to AP - */ - public boolean reassociateCommand() { - return sendSyncMessage(CMD_REASSOCIATE).boolValue; - } - - /** - * Add a network synchronously - * - * @return network id of the new network - */ - public int addOrUpdateNetwork(WifiConfiguration config) { - return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue; - } - - public List<WifiConfiguration> getConfiguredNetworks() { - return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList; - } - - /** - * Delete a network - * - * @param networkId id of the network to be removed - */ - public boolean removeNetwork(int networkId) { - return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue; - } - - private class EnableNetParams { - private int netId; - private boolean disableOthers; - EnableNetParams(int n, boolean b) { - netId = n; - disableOthers = b; - } - } - /** - * Enable a network - * - * @param netId network id of the network - * @param disableOthers true, if all other networks have to be disabled - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean enableNetwork(int netId, boolean disableOthers) { - return sendSyncMessage(CMD_ENABLE_NETWORK, - new EnableNetParams(netId, disableOthers)).boolValue; - } - - /** - * Disable a network - * - * @param netId network id of the network - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public boolean disableNetwork(int netId) { - return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue; - } - - /** - * Blacklist a BSSID. This will avoid the AP if there are - * alternate APs to connect - * - * @param bssid BSSID of the network - */ - public void addToBlacklist(String bssid) { - sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid)); - } - - /** - * Clear the blacklist list - * - */ - public void clearBlacklist() { - sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST)); - } - - /** - * Get detailed status of the connection - * - * @return Example status result - * bssid=aa:bb:cc:dd:ee:ff - * ssid=TestNet - * id=3 - * pairwise_cipher=NONE - * group_cipher=NONE - * key_mgmt=NONE - * wpa_state=COMPLETED - * ip_address=X.X.X.X - */ - public String status() { - return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue; - } - - public void enableRssiPolling(boolean enabled) { - sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0)); - } - /** - * Get RSSI to currently connected network - * - * @return RSSI value, -1 on failure - */ - public int getRssi() { - return sendSyncMessage(CMD_GET_RSSI).intValue; - } - - /** - * Get approx RSSI to currently connected network - * - * @return RSSI value, -1 on failure - */ - public int getRssiApprox() { - return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue; - } - - /** - * Get link speed to currently connected network - * - * @return link speed, -1 on failure - */ - public int getLinkSpeed() { - return sendSyncMessage(CMD_GET_LINK_SPEED).intValue; - } - - /** - * Get MAC address of radio - * - * @return MAC address, null on failure - */ - public String getMacAddress() { - return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue; - } - - /** - * Start packet filtering - */ - public void startPacketFiltering() { - sendMessage(CMD_START_PACKET_FILTERING); - } - - /** - * Stop packet filtering - */ - public void stopPacketFiltering() { - sendMessage(CMD_STOP_PACKET_FILTERING); - } - - /** - * Set power mode - * @param mode - * DRIVER_POWER_MODE_AUTO - * DRIVER_POWER_MODE_ACTIVE - */ - public void setPowerMode(int mode) { - sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0)); - } - - /** - * Set the number of allowed radio frequency channels from the system - * setting value, if any. - */ - public void setNumAllowedChannels() { - try { - setNumAllowedChannels( - Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); - } catch (Settings.SettingNotFoundException e) { - if (mNumAllowedChannels != 0) { - setNumAllowedChannels(mNumAllowedChannels); - } - // otherwise, use the driver default - } - } - - /** - * Set the number of radio frequency channels that are allowed to be used - * in the current regulatory domain. - * @param numChannels the number of allowed channels. Must be greater than 0 - * and less than or equal to 16. - */ - public void setNumAllowedChannels(int numChannels) { - sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0)); - } - - /** - * Get number of allowed channels - * - * @return channel count, -1 on failure - * - * TODO: this is not a public API and needs to be removed in favor - * of asynchronous reporting. unused for now. - */ - public int getNumAllowedChannels() { - return -1; - } - - /** - * Set bluetooth coex mode: - * - * @param mode - * BLUETOOTH_COEXISTENCE_MODE_ENABLED - * BLUETOOTH_COEXISTENCE_MODE_DISABLED - * BLUETOOTH_COEXISTENCE_MODE_SENSE - */ - public void setBluetoothCoexistenceMode(int mode) { - sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0)); - } - - /** - * Enable or disable Bluetooth coexistence scan mode. When this mode is on, - * some of the low-level scan parameters used by the driver are changed to - * reduce interference with A2DP streaming. - * - * @param isBluetoothPlaying whether to enable or disable this mode - */ - public void setBluetoothScanMode(boolean isBluetoothPlaying) { - sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0)); - } - - /** - * Save configuration on supplicant - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - * - * TODO: deprecate this - */ - public boolean saveConfig() { - return sendSyncMessage(CMD_SAVE_CONFIG).boolValue; - } - - /** - * TODO: doc - */ - public void requestCmWakeLock() { - sendMessage(CMD_REQUEST_CM_WAKELOCK); - } - - /********************************************************* - * Internal private functions - ********************************************************/ - - class SyncReturn { - boolean boolValue; - int intValue; - String stringValue; - Object objValue; - List<WifiConfiguration> configList; - } - - class SyncParams { - Object mParameter; - SyncReturn mSyncReturn; - SyncParams() { - mSyncReturn = new SyncReturn(); - } - SyncParams(Object p) { - mParameter = p; - mSyncReturn = new SyncReturn(); - } - } - - /** - * message.arg2 is reserved to indicate synchronized - * message.obj is used to store SyncParams - */ - private SyncReturn syncedSend(Message msg) { - SyncParams syncParams = (SyncParams) msg.obj; - msg.arg2 = SYNCHRONOUS_CALL; - synchronized(syncParams) { - if (DBG) Log.d(TAG, "syncedSend " + msg); - sendMessage(msg); - try { - syncParams.wait(); - } catch (InterruptedException e) { - Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()"); - return null; - } - } - return syncParams.mSyncReturn; - } - - private SyncReturn sendSyncMessage(Message msg) { - SyncParams syncParams = new SyncParams(); - msg.obj = syncParams; - return syncedSend(msg); - } - - private SyncReturn sendSyncMessage(int what, Object param) { - SyncParams syncParams = new SyncParams(param); - Message msg = obtainMessage(what, syncParams); - return syncedSend(msg); - } - - - private SyncReturn sendSyncMessage(int what) { - return sendSyncMessage(obtainMessage(what)); - } - - private void notifyOnMsgObject(Message msg) { - SyncParams syncParams = (SyncParams) msg.obj; - if (syncParams != null) { - synchronized(syncParams) { - if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg); - syncParams.notify(); - } - } - else { - Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null"); - } - } - - private void setWifiState(int wifiState) { - final int previousWifiState = mWifiState.get(); - - try { - if (wifiState == WIFI_STATE_ENABLED) { - mBatteryStats.noteWifiOn(mLastEnableUid.get()); - } else if (wifiState == WIFI_STATE_DISABLED) { - mBatteryStats.noteWifiOff(mLastEnableUid.get()); - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to note battery stats in wifi"); - } - - mWifiState.set(wifiState); - - if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName()); - - final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); - intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); - mContext.sendStickyBroadcast(intent); - } - - private void setWifiApState(int wifiApState) { - final int previousWifiApState = mWifiApState.get(); - - try { - if (wifiApState == WIFI_AP_STATE_ENABLED) { - mBatteryStats.noteWifiOn(mLastApEnableUid.get()); - } else if (wifiApState == WIFI_AP_STATE_DISABLED) { - mBatteryStats.noteWifiOff(mLastApEnableUid.get()); - } - } catch (RemoteException e) { - Log.d(TAG, "Failed to note battery stats in wifi"); - } - - // Update state - mWifiApState.set(wifiApState); - - if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName()); - - // Broadcast - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState); - intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); - mContext.sendStickyBroadcast(intent); - } - - /** - * Parse the scan result line passed to us by wpa_supplicant (helper). - * @param line the line to parse - * @return the {@link ScanResult} object - */ - private ScanResult parseScanResult(String line) { - ScanResult scanResult = null; - if (line != null) { - /* - * Cache implementation (LinkedHashMap) is not synchronized, thus, - * must synchronized here! - */ - synchronized (mScanResultCache) { - String[] result = scanResultPattern.split(line); - if (3 <= result.length && result.length <= 5) { - String bssid = result[0]; - // bssid | frequency | level | flags | ssid - int frequency; - int level; - try { - frequency = Integer.parseInt(result[1]); - level = Integer.parseInt(result[2]); - /* some implementations avoid negative values by adding 256 - * so we need to adjust for that here. - */ - if (level > 0) level -= 256; - } catch (NumberFormatException e) { - frequency = 0; - level = 0; - } - - /* - * The formatting of the results returned by - * wpa_supplicant is intended to make the fields - * line up nicely when printed, - * not to make them easy to parse. So we have to - * apply some heuristics to figure out which field - * is the SSID and which field is the flags. - */ - String ssid; - String flags; - if (result.length == 4) { - if (result[3].charAt(0) == '[') { - flags = result[3]; - ssid = ""; - } else { - flags = ""; - ssid = result[3]; - } - } else if (result.length == 5) { - flags = result[3]; - ssid = result[4]; - } else { - // Here, we must have 3 fields: no flags and ssid - // set - flags = ""; - ssid = ""; - } - - // bssid + ssid is the hash key - String key = bssid + ssid; - scanResult = mScanResultCache.get(key); - if (scanResult != null) { - scanResult.level = level; - scanResult.SSID = ssid; - scanResult.capabilities = flags; - scanResult.frequency = frequency; - } else { - // Do not add scan results that have no SSID set - if (0 < ssid.trim().length()) { - scanResult = - new ScanResult( - ssid, bssid, flags, level, frequency); - mScanResultCache.put(key, scanResult); - } - } - } else { - Log.w(TAG, "Misformatted scan result text with " + - result.length + " fields: " + line); - } - } - } - - return scanResult; - } - - /** - * scanResults input format - * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1 - * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2 - */ - private void setScanResults(String scanResults) { - if (scanResults == null) { - return; - } - - List<ScanResult> scanList = new ArrayList<ScanResult>(); - - int lineCount = 0; - - int scanResultsLen = scanResults.length(); - // Parse the result string, keeping in mind that the last line does - // not end with a newline. - for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) { - if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') { - ++lineCount; - - if (lineCount == 1) { - lineBeg = lineEnd + 1; - continue; - } - if (lineEnd > lineBeg) { - String line = scanResults.substring(lineBeg, lineEnd); - ScanResult scanResult = parseScanResult(line); - if (scanResult != null) { - scanList.add(scanResult); - } else { - Log.w(TAG, "misformatted scan result for: " + line); - } - } - lineBeg = lineEnd + 1; - } - } - - mScanResults = scanList; - } - - private void checkAndSetNotification() { - // If we shouldn't place a notification on available networks, then - // don't bother doing any of the following - if (!mNotificationEnabled) return; - - State state = mNetworkInfo.getState(); - if ((state == NetworkInfo.State.DISCONNECTED) - || (state == NetworkInfo.State.UNKNOWN)) { - // Look for an open network - List<ScanResult> scanResults = mScanResults; - if (scanResults != null) { - int numOpenNetworks = 0; - for (int i = scanResults.size() - 1; i >= 0; i--) { - ScanResult scanResult = scanResults.get(i); - - if (TextUtils.isEmpty(scanResult.capabilities)) { - numOpenNetworks++; - } - } - - if (numOpenNetworks > 0) { - if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { - /* - * We've scanned continuously at least - * NUM_SCANS_BEFORE_NOTIFICATION times. The user - * probably does not have a remembered network in range, - * since otherwise supplicant would have tried to - * associate and thus resetting this counter. - */ - setNotificationVisible(true, numOpenNetworks, false, 0); - } - return; - } - } - } - - // No open networks in range, remove the notification - setNotificationVisible(false, 0, false, 0); - } - - /** - * Display or don't display a notification that there are open Wi-Fi networks. - * @param visible {@code true} if notification should be visible, {@code false} otherwise - * @param numNetworks the number networks seen - * @param force {@code true} to force notification to be shown/not-shown, - * even if it is already shown/not-shown. - * @param delay time in milliseconds after which the notification should be made - * visible or invisible. - */ - private void setNotificationVisible(boolean visible, int numNetworks, boolean force, - int delay) { - - // Since we use auto cancel on the notification, when the - // mNetworksAvailableNotificationShown is true, the notification may - // have actually been canceled. However, when it is false we know - // for sure that it is not being shown (it will not be shown any other - // place than here) - - // If it should be hidden and it is already hidden, then noop - if (!visible && !mNotificationShown && !force) { - return; - } - - Message message; - if (visible) { - - // Not enough time has passed to show the notification again - if (System.currentTimeMillis() < mNotificationRepeatTime) { - return; - } - - if (mNotification == null) { - // Cache the Notification mainly so we can remove the - // EVENT_NOTIFICATION_CHANGED message with this Notification from - // the queue later - mNotification = new Notification(); - mNotification.when = 0; - mNotification.icon = ICON_NETWORKS_AVAILABLE; - mNotification.flags = Notification.FLAG_AUTO_CANCEL; - mNotification.contentIntent = PendingIntent.getActivity(mContext, 0, - new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0); - } - - CharSequence title = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available, numNetworks); - CharSequence details = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available_detailed, numNetworks); - mNotification.tickerText = title; - mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); - - mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; - - message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, - ICON_NETWORKS_AVAILABLE, mNotification); - - } else { - - // Remove any pending messages to show the notification - mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); - - message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, - ICON_NETWORKS_AVAILABLE); - } - - mCsHandler.sendMessageDelayed(message, delay); - - mNotificationShown = visible; - } - - private void configureNetworkProperties() { - try { - mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName)); - } catch (SocketException e) { - Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName + - ". e=" + e); - return; - } catch (NullPointerException e) { - Log.e(TAG, "NPE creating NetworkInterface. e=" + e); - return; - } - // TODO - fix this for v6 - try { - mNetworkProperties.addAddress(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e); - } - - try { - mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray( - mDhcpInfo.gateway))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e); - } - - try { - mNetworkProperties.addDns(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.dns1))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e); - } - try { - mNetworkProperties.addDns(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.dns2))); - - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e); - } - // TODO - add proxy info - } - - - private void checkUseStaticIp() { - mUseStaticIp = false; - final ContentResolver cr = mContext.getContentResolver(); - try { - if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) { - return; - } - } catch (Settings.SettingNotFoundException e) { - return; - } - - try { - String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP); - if (addr != null) { - mDhcpInfo.ipAddress = stringToIpAddr(addr); - } else { - return; - } - addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY); - if (addr != null) { - mDhcpInfo.gateway = stringToIpAddr(addr); - } else { - return; - } - addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK); - if (addr != null) { - mDhcpInfo.netmask = stringToIpAddr(addr); - } else { - return; - } - addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1); - if (addr != null) { - mDhcpInfo.dns1 = stringToIpAddr(addr); - } else { - return; - } - addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2); - if (addr != null) { - mDhcpInfo.dns2 = stringToIpAddr(addr); - } else { - mDhcpInfo.dns2 = 0; - } - } catch (UnknownHostException e) { - return; - } - mUseStaticIp = true; - } - - private static int stringToIpAddr(String addrString) throws UnknownHostException { - try { - String[] parts = addrString.split("\\."); - if (parts.length != 4) { - throw new UnknownHostException(addrString); - } - - int a = Integer.parseInt(parts[0]) ; - int b = Integer.parseInt(parts[1]) << 8; - int c = Integer.parseInt(parts[2]) << 16; - int d = Integer.parseInt(parts[3]) << 24; - - return a | b | c | d; - } catch (NumberFormatException ex) { - throw new UnknownHostException(addrString); - } - } - - private int getMaxDhcpRetries() { - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT, - DEFAULT_MAX_DHCP_RETRIES); - } - - private class SettingsObserver extends ContentObserver { - public SettingsObserver(Handler handler) { - super(handler); - ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_USE_STATIC_IP), false, this); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_STATIC_IP), false, this); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_STATIC_GATEWAY), false, this); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_STATIC_NETMASK), false, this); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_STATIC_DNS1), false, this); - cr.registerContentObserver(Settings.System.getUriFor( - Settings.System.WIFI_STATIC_DNS2), false, this); - } - + private class WifiStateReceiver extends BroadcastReceiver { @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - - boolean wasStaticIp = mUseStaticIp; - int oIp, oGw, oMsk, oDns1, oDns2; - oIp = oGw = oMsk = oDns1 = oDns2 = 0; - if (wasStaticIp) { - oIp = mDhcpInfo.ipAddress; - oGw = mDhcpInfo.gateway; - oMsk = mDhcpInfo.netmask; - oDns1 = mDhcpInfo.dns1; - oDns2 = mDhcpInfo.dns2; - } - checkUseStaticIp(); - - if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) { - return; - } - - boolean changed = - (wasStaticIp != mUseStaticIp) || - (wasStaticIp && ( - oIp != mDhcpInfo.ipAddress || - oGw != mDhcpInfo.gateway || - oMsk != mDhcpInfo.netmask || - oDns1 != mDhcpInfo.dns1 || - oDns2 != mDhcpInfo.dns2)); - - if (changed) { - sendMessage(CMD_RECONFIGURE_IP); - if (mUseStaticIp) { - mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); - } + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + mNetworkProperties = (NetworkProperties) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_PROPERTIES); + Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) { + mNetworkProperties = (NetworkProperties) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_PROPERTIES); + Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); + msg.sendToTarget(); } } } - - /** - * Clears variables related to tracking whether a notification has been - * shown recently. - * <p> - * After calling this method, the timer that prevents notifications from - * being shown too often will be cleared. - */ - private void resetNotificationTimer() { - mNotificationRepeatTime = 0; - mNumScansSinceNetworkStateChange = 0; - } - - - /** - * Whether to disable coexistence mode while obtaining IP address. This - * logic will return true only if the current bluetooth - * headset/handsfree state is disconnected. This means if it is in an - * error state, we will NOT disable coexistence mode to err on the side - * of safety. - * - * @return Whether to disable coexistence mode. - */ - private boolean shouldDisableCoexistenceMode() { - int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset()); - return state == BluetoothHeadset.STATE_DISCONNECTED; - } - - private void checkIsBluetoothPlaying() { - boolean isBluetoothPlaying = false; - Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks(); - - for (BluetoothDevice device : connected) { - if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) { - isBluetoothPlaying = true; - break; - } - } - setBluetoothScanMode(isBluetoothPlaying); - } - - private void sendScanResultsAvailableBroadcast() { - if (!ActivityManagerNative.isSystemReady()) return; - - mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); - } - - private void sendRssiChangeBroadcast(final int newRssi) { - if (!ActivityManagerNative.isSystemReady()) return; - - Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); - intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); - mContext.sendBroadcast(intent); - } - - private void sendNetworkStateChangeBroadcast(String bssid) { - Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo); - if (bssid != null) - intent.putExtra(WifiManager.EXTRA_BSSID, bssid); - mContext.sendStickyBroadcast(intent); - } - - private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) { - Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state); - if (failedAuth) { - intent.putExtra( - WifiManager.EXTRA_SUPPLICANT_ERROR, - WifiManager.ERROR_AUTHENTICATING); - } - mContext.sendStickyBroadcast(intent); - } - - private void sendSupplicantConnectionChangedBroadcast(boolean connected) { - if (!ActivityManagerNative.isSystemReady()) return; - - Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); - intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected); - mContext.sendBroadcast(intent); - } - - /** - * Record the detailed state of a network, and if it is a - * change from the previous state, send a notification to - * any listeners. - * @param state the new @{code DetailedState} - */ - private void setDetailedState(NetworkInfo.DetailedState state) { - Log.d(TAG, "setDetailed state, old =" - + mNetworkInfo.getDetailedState() + " and new state=" + state); - if (state != mNetworkInfo.getDetailedState()) { - mNetworkInfo.setDetailedState(state, null, null); - Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); - } - } - - private static String removeDoubleQuotes(String string) { - if (string.length() <= 2) return ""; - return string.substring(1, string.length() - 1); - } - - private static String convertToQuotedString(String string) { - return "\"" + string + "\""; - } - - private static String makeString(BitSet set, String[] strings) { - StringBuffer buf = new StringBuffer(); - int nextSetBit = -1; - - /* Make sure all set bits are in [0, strings.length) to avoid - * going out of bounds on strings. (Shouldn't happen, but...) */ - set = set.get(0, strings.length); - - while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { - buf.append(strings[nextSetBit].replace('_', '-')).append(' '); - } - - // remove trailing space - if (set.cardinality() > 0) { - buf.setLength(buf.length() - 1); - } - - return buf.toString(); - } - - private static int lookupString(String string, String[] strings) { - int size = strings.length; - - string = string.replace('-', '_'); - - for (int i = 0; i < size; i++) - if (string.equals(strings[i])) - return i; - - // if we ever get here, we should probably add the - // value to WifiConfiguration to reflect that it's - // supported by the WPA supplicant - Log.w(TAG, "Failed to look-up a string: " + string); - - return -1; - } - - private int addOrUpdateNetworkNative(WifiConfiguration config) { - /* - * If the supplied networkId is -1, we create a new empty - * network configuration. Otherwise, the networkId should - * refer to an existing configuration. - */ - int netId = config.networkId; - boolean newNetwork = netId == -1; - // networkId of -1 means we want to create a new network - - if (newNetwork) { - netId = WifiNative.addNetworkCommand(); - if (netId < 0) { - Log.e(TAG, "Failed to add a network!"); - return -1; - } - } - - setVariables: { - - if (config.SSID != null && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.ssidVarName, - config.SSID)) { - Log.d(TAG, "failed to set SSID: "+config.SSID); - break setVariables; - } - - if (config.BSSID != null && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.bssidVarName, - config.BSSID)) { - Log.d(TAG, "failed to set BSSID: "+config.BSSID); - break setVariables; - } - - String allowedKeyManagementString = - makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); - if (config.allowedKeyManagement.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.KeyMgmt.varName, - allowedKeyManagementString)) { - Log.d(TAG, "failed to set key_mgmt: "+ - allowedKeyManagementString); - break setVariables; - } - - String allowedProtocolsString = - makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); - if (config.allowedProtocols.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.Protocol.varName, - allowedProtocolsString)) { - Log.d(TAG, "failed to set proto: "+ - allowedProtocolsString); - break setVariables; - } - - String allowedAuthAlgorithmsString = - makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); - if (config.allowedAuthAlgorithms.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.AuthAlgorithm.varName, - allowedAuthAlgorithmsString)) { - Log.d(TAG, "failed to set auth_alg: "+ - allowedAuthAlgorithmsString); - break setVariables; - } - - String allowedPairwiseCiphersString = - makeString(config.allowedPairwiseCiphers, - WifiConfiguration.PairwiseCipher.strings); - if (config.allowedPairwiseCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.PairwiseCipher.varName, - allowedPairwiseCiphersString)) { - Log.d(TAG, "failed to set pairwise: "+ - allowedPairwiseCiphersString); - break setVariables; - } - - String allowedGroupCiphersString = - makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); - if (config.allowedGroupCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.GroupCipher.varName, - allowedGroupCiphersString)) { - Log.d(TAG, "failed to set group: "+ - allowedGroupCiphersString); - break setVariables; - } - - // Prevent client screw-up by passing in a WifiConfiguration we gave it - // by preventing "*" as a key. - if (config.preSharedKey != null && !config.preSharedKey.equals("*") && - !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.pskVarName, - config.preSharedKey)) { - Log.d(TAG, "failed to set psk: "+config.preSharedKey); - break setVariables; - } - - boolean hasSetKey = false; - if (config.wepKeys != null) { - for (int i = 0; i < config.wepKeys.length; i++) { - // Prevent client screw-up by passing in a WifiConfiguration we gave it - // by preventing "*" as a key. - if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.wepKeyVarNames[i], - config.wepKeys[i])) { - Log.d(TAG, - "failed to set wep_key"+i+": " + - config.wepKeys[i]); - break setVariables; - } - hasSetKey = true; - } - } - } - - if (hasSetKey) { - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.wepTxKeyIdxVarName, - Integer.toString(config.wepTxKeyIndex))) { - Log.d(TAG, - "failed to set wep_tx_keyidx: "+ - config.wepTxKeyIndex); - break setVariables; - } - } - - if (!WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.priorityVarName, - Integer.toString(config.priority))) { - Log.d(TAG, config.SSID + ": failed to set priority: " - +config.priority); - break setVariables; - } - - if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( - netId, - WifiConfiguration.hiddenSSIDVarName, - Integer.toString(config.hiddenSSID ? 1 : 0))) { - Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ - config.hiddenSSID); - break setVariables; - } - - for (WifiConfiguration.EnterpriseField field - : config.enterpriseFields) { - String varName = field.varName(); - String value = field.value(); - if (value != null) { - if (field != config.eap) { - value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); - } - if (!WifiNative.setNetworkVariableCommand( - netId, - varName, - value)) { - Log.d(TAG, config.SSID + ": failed to set " + varName + - ": " + value); - break setVariables; - } - } - } - return netId; - } - - if (newNetwork) { - WifiNative.removeNetworkCommand(netId); - Log.d(TAG, - "Failed to set a network variable, removed network: " - + netId); - } - - return -1; - } - - private List<WifiConfiguration> getConfiguredNetworksNative() { - String listStr = WifiNative.listNetworksCommand(); - - List<WifiConfiguration> networks = - new ArrayList<WifiConfiguration>(); - if (listStr == null) - return networks; - - String[] lines = listStr.split("\n"); - // Skip the first line, which is a header - for (int i = 1; i < lines.length; i++) { - String[] result = lines[i].split("\t"); - // network-id | ssid | bssid | flags - WifiConfiguration config = new WifiConfiguration(); - try { - config.networkId = Integer.parseInt(result[0]); - } catch(NumberFormatException e) { - continue; - } - if (result.length > 3) { - if (result[3].indexOf("[CURRENT]") != -1) - config.status = WifiConfiguration.Status.CURRENT; - else if (result[3].indexOf("[DISABLED]") != -1) - config.status = WifiConfiguration.Status.DISABLED; - else - config.status = WifiConfiguration.Status.ENABLED; - } else { - config.status = WifiConfiguration.Status.ENABLED; - } - readNetworkVariables(config); - networks.add(config); - } - return networks; - } - - /** - * Read the variables from the supplicant daemon that are needed to - * fill in the WifiConfiguration object. - * - * @param config the {@link WifiConfiguration} object to be filled in. - */ - private void readNetworkVariables(WifiConfiguration config) { - - int netId = config.networkId; - if (netId < 0) - return; - - /* - * TODO: maybe should have a native method that takes an array of - * variable names and returns an array of values. But we'd still - * be doing a round trip to the supplicant daemon for each variable. - */ - String value; - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); - if (!TextUtils.isEmpty(value)) { - config.SSID = removeDoubleQuotes(value); - } else { - config.SSID = null; - } - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); - if (!TextUtils.isEmpty(value)) { - config.BSSID = value; - } else { - config.BSSID = null; - } - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); - config.priority = -1; - if (!TextUtils.isEmpty(value)) { - try { - config.priority = Integer.parseInt(value); - } catch (NumberFormatException ignore) { - } - } - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); - config.hiddenSSID = false; - if (!TextUtils.isEmpty(value)) { - try { - config.hiddenSSID = Integer.parseInt(value) != 0; - } catch (NumberFormatException ignore) { - } - } - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); - config.wepTxKeyIndex = -1; - if (!TextUtils.isEmpty(value)) { - try { - config.wepTxKeyIndex = Integer.parseInt(value); - } catch (NumberFormatException ignore) { - } - } - - for (int i = 0; i < 4; i++) { - value = WifiNative.getNetworkVariableCommand(netId, - WifiConfiguration.wepKeyVarNames[i]); - if (!TextUtils.isEmpty(value)) { - config.wepKeys[i] = value; - } else { - config.wepKeys[i] = null; - } - } - - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); - if (!TextUtils.isEmpty(value)) { - config.preSharedKey = value; - } else { - config.preSharedKey = null; - } - - value = WifiNative.getNetworkVariableCommand(config.networkId, - WifiConfiguration.Protocol.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.Protocol.strings); - if (0 <= index) { - config.allowedProtocols.set(index); - } - } - } - - value = WifiNative.getNetworkVariableCommand(config.networkId, - WifiConfiguration.KeyMgmt.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.KeyMgmt.strings); - if (0 <= index) { - config.allowedKeyManagement.set(index); - } - } - } - - value = WifiNative.getNetworkVariableCommand(config.networkId, - WifiConfiguration.AuthAlgorithm.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.AuthAlgorithm.strings); - if (0 <= index) { - config.allowedAuthAlgorithms.set(index); - } - } - } - - value = WifiNative.getNetworkVariableCommand(config.networkId, - WifiConfiguration.PairwiseCipher.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.PairwiseCipher.strings); - if (0 <= index) { - config.allowedPairwiseCiphers.set(index); - } - } - } - - value = WifiNative.getNetworkVariableCommand(config.networkId, - WifiConfiguration.GroupCipher.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.GroupCipher.strings); - if (0 <= index) { - config.allowedGroupCiphers.set(index); - } - } - } - - for (WifiConfiguration.EnterpriseField field : - config.enterpriseFields) { - value = WifiNative.getNetworkVariableCommand(netId, - field.varName()); - if (!TextUtils.isEmpty(value)) { - if (field != config.eap) value = removeDoubleQuotes(value); - field.setValue(value); - } - } - - } - - /** - * Poll for info not reported via events - * RSSI & Linkspeed - */ - private void requestPolledInfo() { - int newRssi = WifiNative.getRssiCommand(); - if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values - /* some implementations avoid negative values by adding 256 - * so we need to adjust for that here. - */ - if (newRssi > 0) newRssi -= 256; - mWifiInfo.setRssi(newRssi); - /* - * Rather then sending the raw RSSI out every time it - * changes, we precalculate the signal level that would - * be displayed in the status bar, and only send the - * broadcast if that much more coarse-grained number - * changes. This cuts down greatly on the number of - * broadcasts, at the cost of not mWifiInforming others - * interested in RSSI of all the changes in signal - * level. - */ - // TODO: The second arg to the call below needs to be a symbol somewhere, but - // it's actually the size of an array of icons that's private - // to StatusBar Policy. - int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); - if (newSignalLevel != mLastSignalLevel) { - sendRssiChangeBroadcast(newRssi); - } - mLastSignalLevel = newSignalLevel; - } else { - mWifiInfo.setRssi(-200); - } - int newLinkSpeed = WifiNative.getLinkSpeedCommand(); - if (newLinkSpeed != -1) { - mWifiInfo.setLinkSpeed(newLinkSpeed); - } - } - - /** - * Resets the Wi-Fi Connections by clearing any state, resetting any sockets - * using the interface, stopping DHCP & disabling interface - */ - private void handleNetworkDisconnect() { - Log.d(TAG, "Reset connections and stopping DHCP"); - - /* - * Reset connections & stop DHCP - */ - NetworkUtils.resetConnections(mInterfaceName); - - if (!NetworkUtils.stopDhcp(mInterfaceName)) { - Log.e(TAG, "Could not stop DHCP"); - } - - /* Disable interface */ - NetworkUtils.disableInterface(mInterfaceName); - - /* send event to CM & network change broadcast */ - setDetailedState(DetailedState.DISCONNECTED); - sendNetworkStateChangeBroadcast(mLastBssid); - - /* Reset data structures */ - mWifiInfo.setIpAddress(0); - mWifiInfo.setBSSID(null); - mWifiInfo.setSSID(null); - mWifiInfo.setNetworkId(-1); - - /* Clear network properties */ - mNetworkProperties.clear(); - - mLastBssid= null; - mLastNetworkId = -1; - - } - - - /********************************************************* - * Notifications from WifiMonitor - ********************************************************/ - - /** - * A structure for supplying information about a supplicant state - * change in the STATE_CHANGE event message that comes from the - * WifiMonitor - * thread. - */ - private static class StateChangeResult { - StateChangeResult(int networkId, String BSSID, Object state) { - this.state = state; - this.BSSID = BSSID; - this.networkId = networkId; - } - int networkId; - String BSSID; - Object state; - } - - /** - * Send the tracker a notification that a user-entered password key - * may be incorrect (i.e., caused authentication to fail). - */ - void notifyPasswordKeyMayBeIncorrect() { - sendMessage(PASSWORD_MAY_BE_INCORRECT_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 a connection to the supplicant - * daemon has been established. - */ - 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, - new StateChangeResult(networkId, BSSID, newState))); - } else { - sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, - new StateChangeResult(networkId, BSSID, newState))); - } - } - - /** - * 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 notifyDriverStarted() { - sendMessage(DRIVER_START_EVENT); - } - - void notifyDriverStopped() { - sendMessage(DRIVER_STOP_EVENT); - } - - void notifyDriverHung() { - setWifiEnabled(false); - setWifiEnabled(true); - } - - - /******************************************************** - * HSM states - *******************************************************/ - - class DefaultState extends HierarchicalState { - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - SyncParams syncParams; - switch (message.what) { - /* Synchronous call returns */ - case CMD_PING_SUPPLICANT: - case CMD_START_SCAN: - case CMD_DISCONNECT: - case CMD_RECONNECT: - case CMD_REASSOCIATE: - case CMD_REMOVE_NETWORK: - case CMD_ENABLE_NETWORK: - case CMD_DISABLE_NETWORK: - case CMD_ADD_OR_UPDATE_NETWORK: - case CMD_GET_RSSI: - case CMD_GET_RSSI_APPROX: - case CMD_GET_LINK_SPEED: - case CMD_GET_MAC_ADDR: - case CMD_SAVE_CONFIG: - case CMD_CONNECTION_STATUS: - case CMD_GET_NETWORK_CONFIG: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = false; - syncParams.mSyncReturn.intValue = -1; - syncParams.mSyncReturn.stringValue = null; - syncParams.mSyncReturn.configList = null; - notifyOnMsgObject(message); - } - break; - case CM_CMD_TEARDOWN: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - mTeardownRequested.set(true); - sendMessage(CMD_DISCONNECT); - sendMessage(CMD_STOP_DRIVER); - /* Mark wifi available when CM tears down */ - mNetworkInfo.setIsAvailable(true); - break; - case CM_CMD_RECONNECT: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - mTeardownRequested.set(false); - sendMessage(CMD_START_DRIVER); - sendMessage(CMD_RECONNECT); - break; - case CMD_ENABLE_RSSI_POLL: - mEnableRssiPolling = (message.arg1 == 1); - mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL); - break; - default: - if (DBG) Log.w(TAG, "Unhandled " + message); - break; - } - return HANDLED; - } - } - - class InitialState extends HierarchicalState { - @Override - //TODO: could move logging into a common class - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - // [31-8] Reserved for future use - // [7 - 0] HSM state change - // 50021 wifi_state_changed (custom|1|5) - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - if (WifiNative.isDriverLoaded()) { - transitionTo(mDriverLoadedState); - } - else { - transitionTo(mDriverUnloadedState); - } - } - } - - class DriverLoadingState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - final Message message = new Message(); - message.copyFrom(getCurrentMessage()); - new Thread(new Runnable() { - public void run() { - sWakeLock.acquire(); - //enabling state - switch(message.arg1) { - case WIFI_STATE_ENABLING: - setWifiState(WIFI_STATE_ENABLING); - break; - case WIFI_AP_STATE_ENABLING: - setWifiApState(WIFI_AP_STATE_ENABLING); - break; - } - - if(WifiNative.loadDriver()) { - Log.d(TAG, "Driver load successful"); - sendMessage(CMD_LOAD_DRIVER_SUCCESS); - } else { - Log.e(TAG, "Failed to load driver!"); - switch(message.arg1) { - case WIFI_STATE_ENABLING: - setWifiState(WIFI_STATE_UNKNOWN); - break; - case WIFI_AP_STATE_ENABLING: - setWifiApState(WIFI_AP_STATE_FAILED); - break; - } - sendMessage(CMD_LOAD_DRIVER_FAILURE); - } - sWakeLock.release(); - } - }).start(); - } - - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - switch (message.what) { - case CMD_LOAD_DRIVER_SUCCESS: - transitionTo(mDriverLoadedState); - break; - case CMD_LOAD_DRIVER_FAILURE: - transitionTo(mDriverFailedState); - 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_POWER_MODE: - case CMD_SET_BLUETOOTH_COEXISTENCE: - case CMD_SET_BLUETOOTH_SCAN_MODE: - case CMD_SET_NUM_ALLOWED_CHANNELS: - 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 DriverLoadedState extends HierarchicalState { - @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_UNLOAD_DRIVER: - transitionTo(mDriverUnloadingState); - break; - case CMD_START_SUPPLICANT: - if(WifiNative.startSupplicant()) { - Log.d(TAG, "Supplicant start successful"); - mWifiMonitor.startMonitoring(); - setWifiState(WIFI_STATE_ENABLED); - transitionTo(mWaitForSupState); - } else { - Log.e(TAG, "Failed to start supplicant!"); - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); - } - break; - case CMD_START_AP: - try { - nwService.startAccessPoint((WifiConfiguration) message.obj, - mInterfaceName, - SOFTAP_IFACE); - } catch(Exception e) { - Log.e(TAG, "Exception in startAccessPoint()"); - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); - break; - } - Log.d(TAG, "Soft AP start successful"); - setWifiApState(WIFI_AP_STATE_ENABLED); - transitionTo(mSoftApStartedState); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DriverUnloadingState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - final Message message = new Message(); - message.copyFrom(getCurrentMessage()); - new Thread(new Runnable() { - public void run() { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - sWakeLock.acquire(); - if(WifiNative.unloadDriver()) { - Log.d(TAG, "Driver unload successful"); - sendMessage(CMD_UNLOAD_DRIVER_SUCCESS); - - switch(message.arg1) { - case WIFI_STATE_DISABLED: - case WIFI_STATE_UNKNOWN: - setWifiState(message.arg1); - break; - case WIFI_AP_STATE_DISABLED: - case WIFI_AP_STATE_FAILED: - setWifiApState(message.arg1); - break; - } - } else { - Log.e(TAG, "Failed to unload driver!"); - sendMessage(CMD_UNLOAD_DRIVER_FAILURE); - - switch(message.arg1) { - case WIFI_STATE_DISABLED: - case WIFI_STATE_UNKNOWN: - setWifiState(WIFI_STATE_UNKNOWN); - break; - case WIFI_AP_STATE_DISABLED: - case WIFI_AP_STATE_FAILED: - setWifiApState(WIFI_AP_STATE_FAILED); - break; - } - } - sWakeLock.release(); - } - }).start(); - } - - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - switch (message.what) { - case CMD_UNLOAD_DRIVER_SUCCESS: - transitionTo(mDriverUnloadedState); - break; - case CMD_UNLOAD_DRIVER_FAILURE: - transitionTo(mDriverFailedState); - 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_POWER_MODE: - case CMD_SET_BLUETOOTH_COEXISTENCE: - case CMD_SET_BLUETOOTH_SCAN_MODE: - case CMD_SET_NUM_ALLOWED_CHANNELS: - 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 DriverUnloadedState extends HierarchicalState { - @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_LOAD_DRIVER: - transitionTo(mDriverLoadingState); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DriverFailedState extends HierarchicalState { - @Override - public void enter() { - Log.e(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"); - return NOT_HANDLED; - } - } - - - class WaitForSupState extends HierarchicalState { - @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 SUP_CONNECTION_EVENT: - Log.d(TAG, "Supplicant connection established"); - mSupplicantStateTracker.resetSupplicantState(); - /* Initialize data structures */ - resetNotificationTimer(); - setTeardownRequested(false); - mLastBssid = null; - mLastNetworkId = -1; - mLastSignalLevel = -1; - - mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand()); - - //TODO: initialize and fix multicast filtering - //mWM.initializeMulticastFiltering(); - - if (mBluetoothA2dp == null) { - mBluetoothA2dp = new BluetoothA2dp(mContext); - } - checkIsBluetoothPlaying(); - - checkUseStaticIp(); - sendSupplicantConnectionChangedBroadcast(true); - transitionTo(mDriverSupReadyState); - break; - case CMD_STOP_SUPPLICANT: - Log.d(TAG, "Stop supplicant received"); - WifiNative.stopSupplicant(); - transitionTo(mDriverLoadedState); - break; - /* Fail soft ap when waiting for supplicant start */ - case CMD_START_AP: - Log.d(TAG, "Failed to start soft AP with a running supplicant"); - setWifiApState(WIFI_AP_STATE_FAILED); - break; - case CMD_START_DRIVER: - case CMD_STOP_DRIVER: - case CMD_SET_SCAN_MODE: - case CMD_SET_SCAN_TYPE: - case CMD_SET_POWER_MODE: - case CMD_SET_BLUETOOTH_COEXISTENCE: - case CMD_SET_BLUETOOTH_SCAN_MODE: - case CMD_SET_NUM_ALLOWED_CHANNELS: - case CMD_START_PACKET_FILTERING: - case CMD_STOP_PACKET_FILTERING: - deferMessage(message); - break; - case CMD_STOP_AP: - case CMD_START_SUPPLICANT: - case CMD_UNLOAD_DRIVER: - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DriverSupReadyState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - /* Initialize for connect mode operation at start */ - mIsScanMode = false; - } - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - SyncParams syncParams; - switch(message.what) { - case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ - Log.d(TAG, "Stop supplicant received"); - WifiNative.stopSupplicant(); - //$FALL-THROUGH$ - case SUP_DISCONNECTION_EVENT: /* Supplicant died */ - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - //Remove any notifications on disconnection - setNotificationVisible(false, 0, false, 0); - WifiNative.closeSupplicantConnection(); - handleNetworkDisconnect(); - sendSupplicantConnectionChangedBroadcast(false); - mSupplicantStateTracker.resetSupplicantState(); - transitionTo(mDriverLoadedState); - - /* When supplicant dies, unload driver and enter failed state */ - //TODO: consider bringing up supplicant again - if (message.what == SUP_DISCONNECTION_EVENT) { - Log.d(TAG, "Supplicant died, unloading driver"); - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); - } - break; - case CMD_START_DRIVER: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - WifiNative.startDriverCommand(); - transitionTo(mDriverStartingState); - break; - case SCAN_RESULTS_EVENT: - setScanResults(WifiNative.scanResultsCommand()); - sendScanResultsAvailableBroadcast(); - checkAndSetNotification(); - break; - case CMD_PING_SUPPLICANT: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.pingCommand(); - notifyOnMsgObject(message); - break; - case CMD_ADD_OR_UPDATE_NETWORK: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - syncParams = (SyncParams) message.obj; - WifiConfiguration config = (WifiConfiguration) syncParams.mParameter; - syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config); - notifyOnMsgObject(message); - break; - case CMD_REMOVE_NETWORK: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand( - message.arg1); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.removeNetworkCommand(message.arg1); - } - break; - case CMD_ENABLE_NETWORK: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter; - syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand( - enableNetParams.netId, enableNetParams.disableOthers); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1); - } - break; - case CMD_DISABLE_NETWORK: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand( - message.arg1); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.disableNetworkCommand(message.arg1); - } - break; - case CMD_BLACKLIST_NETWORK: - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - WifiNative.addToBlacklistCommand((String)message.obj); - break; - case CMD_CLEAR_BLACKLIST: - WifiNative.clearBlacklistCommand(); - break; - case CMD_GET_NETWORK_CONFIG: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.configList = getConfiguredNetworksNative(); - notifyOnMsgObject(message); - break; - case CMD_SAVE_CONFIG: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.saveConfigCommand(); - } - // Inform the backup manager about a data change - IBackupManager ibm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - if (ibm != null) { - try { - ibm.dataChanged("com.android.providers.settings"); - } catch (Exception e) { - // Try again later - } - } - break; - case CMD_CONNECTION_STATUS: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.stringValue = WifiNative.statusCommand(); - notifyOnMsgObject(message); - break; - case CMD_GET_MAC_ADDR: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand(); - notifyOnMsgObject(message); - break; - /* Cannot start soft AP while in client mode */ - case CMD_START_AP: - Log.d(TAG, "Failed to start soft AP with a running supplicant"); - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - setWifiApState(WIFI_AP_STATE_FAILED); - break; - case CMD_SET_SCAN_MODE: - mIsScanMode = (message.arg1 == SCAN_ONLY_MODE); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class DriverStartingState extends HierarchicalState { - @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 DRIVER_START_EVENT: - transitionTo(mDriverStartedState); - break; - /* Queue driver commands & connection events */ - case CMD_START_DRIVER: - case CMD_STOP_DRIVER: - case SUPPLICANT_STATE_CHANGE_EVENT: - case NETWORK_CONNECTION_EVENT: - case NETWORK_DISCONNECTION_EVENT: - case PASSWORD_MAY_BE_INCORRECT_EVENT: - case CMD_SET_SCAN_TYPE: - case CMD_SET_POWER_MODE: - case CMD_SET_BLUETOOTH_COEXISTENCE: - case CMD_SET_BLUETOOTH_SCAN_MODE: - case CMD_SET_NUM_ALLOWED_CHANNELS: - case CMD_START_PACKET_FILTERING: - case CMD_STOP_PACKET_FILTERING: - deferMessage(message); - break; - /* Queue the asynchronous version of these commands */ - case CMD_START_SCAN: - case CMD_DISCONNECT: - case CMD_REASSOCIATE: - case CMD_RECONNECT: - if (message.arg2 != SYNCHRONOUS_CALL) { - deferMessage(message); - } - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DriverStartedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - try { - mBatteryStats.noteWifiRunning(); - } catch (RemoteException ignore) {} - - /* Initialize channel count */ - setNumAllowedChannels(); - - if (mIsScanMode) { - WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); - WifiNative.disconnectCommand(); - transitionTo(mScanModeState); - } else { - WifiNative.setScanResultHandlingCommand(CONNECT_MODE); - /* If supplicant has already connected, before we could finish establishing - * the control channel connection, we miss all the supplicant events. - * Disconnect and reconnect when driver has started to ensure we receive - * all supplicant events. - * - * TODO: This is a bit unclean, ideally the supplicant should never - * connect until told to do so by the framework - */ - WifiNative.disconnectCommand(); - WifiNative.reconnectCommand(); - transitionTo(mConnectModeState); - } - } - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - SyncParams syncParams; - switch(message.what) { - case CMD_SET_SCAN_TYPE: - if (message.arg1 == SCAN_ACTIVE) { - WifiNative.setScanModeCommand(true); - } else { - WifiNative.setScanModeCommand(false); - } - break; - case CMD_SET_POWER_MODE: - WifiNative.setPowerModeCommand(message.arg1); - break; - case CMD_SET_BLUETOOTH_COEXISTENCE: - WifiNative.setBluetoothCoexistenceModeCommand(message.arg1); - break; - case CMD_SET_BLUETOOTH_SCAN_MODE: - WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1); - break; - case CMD_SET_NUM_ALLOWED_CHANNELS: - mNumAllowedChannels = message.arg1; - WifiNative.setNumAllowedChannelsCommand(message.arg1); - break; - case CMD_START_DRIVER: - /* Ignore another driver start */ - break; - case CMD_STOP_DRIVER: - WifiNative.stopDriverCommand(); - transitionTo(mDriverStoppingState); - break; - case CMD_REQUEST_CM_WAKELOCK: - if (mCm == null) { - mCm = (ConnectivityManager)mContext.getSystemService( - Context.CONNECTIVITY_SERVICE); - } - mCm.requestNetworkTransitionWakelock(TAG); - break; - case CMD_START_PACKET_FILTERING: - WifiNative.startPacketFiltering(); - break; - case CMD_STOP_PACKET_FILTERING: - WifiNative.stopPacketFiltering(); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - @Override - public void exit() { - if (DBG) Log.d(TAG, getName() + "\n"); - try { - mBatteryStats.noteWifiStopped(); - } catch (RemoteException ignore) { } - } - } - - class DriverStoppingState extends HierarchicalState { - @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 DRIVER_STOP_EVENT: - transitionTo(mDriverStoppedState); - break; - /* Queue driver commands */ - case CMD_START_DRIVER: - case CMD_STOP_DRIVER: - case CMD_SET_SCAN_TYPE: - case CMD_SET_POWER_MODE: - case CMD_SET_BLUETOOTH_COEXISTENCE: - case CMD_SET_BLUETOOTH_SCAN_MODE: - case CMD_SET_NUM_ALLOWED_CHANNELS: - case CMD_START_PACKET_FILTERING: - case CMD_STOP_PACKET_FILTERING: - deferMessage(message); - break; - /* Queue the asynchronous version of these commands */ - case CMD_START_SCAN: - case CMD_DISCONNECT: - case CMD_REASSOCIATE: - case CMD_RECONNECT: - if (message.arg2 != SYNCHRONOUS_CALL) { - deferMessage(message); - } - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DriverStoppedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - // Take down any open network notifications on driver stop - setNotificationVisible(false, 0, false, 0); - } - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - return NOT_HANDLED; - } - } - - class ScanModeState extends HierarchicalState { - @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"); - SyncParams syncParams; - switch(message.what) { - case CMD_SET_SCAN_MODE: - if (message.arg1 == SCAN_ONLY_MODE) { - /* Ignore */ - return HANDLED; - } else { - WifiNative.setScanResultHandlingCommand(message.arg1); - WifiNative.reconnectCommand(); - mIsScanMode = false; - transitionTo(mDisconnectedState); - } - break; - case CMD_START_SCAN: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.scanCommand( - message.arg1 == SCAN_ACTIVE); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); - } - break; - /* Ignore */ - case CMD_DISCONNECT: - case CMD_RECONNECT: - case CMD_REASSOCIATE: - case SUPPLICANT_STATE_CHANGE_EVENT: - case NETWORK_CONNECTION_EVENT: - case NETWORK_DISCONNECTION_EVENT: - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class ConnectModeState extends HierarchicalState { - @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"); - SyncParams syncParams; - StateChangeResult stateChangeResult; - switch(message.what) { - case PASSWORD_MAY_BE_INCORRECT_EVENT: - mPasswordKeyMayBeIncorrect = true; - break; - case SUPPLICANT_STATE_CHANGE_EVENT: - stateChangeResult = (StateChangeResult) message.obj; - mSupplicantStateTracker.handleEvent(stateChangeResult); - break; - case CMD_START_SCAN: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = true; - notifyOnMsgObject(message); - } - /* We need to set scan type in completed state */ - Message newMsg = obtainMessage(); - newMsg.copyFrom(message); - mSupplicantStateTracker.sendMessage(newMsg); - break; - /* Do a redundant disconnect without transition */ - case CMD_DISCONNECT: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.disconnectCommand(); - } - break; - case CMD_RECONNECT: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.reconnectCommand(); - } - break; - case CMD_REASSOCIATE: - if (message.arg2 == SYNCHRONOUS_CALL) { - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.reassociateCommand(); - } - break; - case 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: - Log.d(TAG,"Network connection established"); - stateChangeResult = (StateChangeResult) message.obj; - - /* Remove any notifications */ - setNotificationVisible(false, 0, false, 0); - resetNotificationTimer(); - - mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID); - mWifiInfo.setNetworkId(stateChangeResult.networkId); - mLastNetworkId = stateChangeResult.networkId; - - /* send event to CM & network change broadcast */ - setDetailedState(DetailedState.OBTAINING_IPADDR); - sendNetworkStateChangeBroadcast(mLastBssid); - - transitionTo(mConnectingState); - break; - case NETWORK_DISCONNECTION_EVENT: - Log.d(TAG,"Network connection lost"); - handleNetworkDisconnect(); - transitionTo(mDisconnectedState); - break; - case CMD_GET_RSSI: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand(); - notifyOnMsgObject(message); - break; - case CMD_GET_RSSI_APPROX: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand(); - notifyOnMsgObject(message); - break; - case CMD_GET_LINK_SPEED: - syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand(); - notifyOnMsgObject(message); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class ConnectingState extends HierarchicalState { - boolean modifiedBluetoothCoexistenceMode; - int powerMode; - Thread mDhcpThread; - - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); - - if (!mUseStaticIp) { - - mDhcpThread = null; - modifiedBluetoothCoexistenceMode = false; - powerMode = DRIVER_POWER_MODE_AUTO; - - if (shouldDisableCoexistenceMode()) { - /* - * There are problems setting the Wi-Fi driver's power - * mode to active when bluetooth coexistence mode is - * enabled or sense. - * <p> - * We set Wi-Fi to active mode when - * obtaining an IP address because we've found - * compatibility issues with some routers with low power - * mode. - * <p> - * In order for this active power mode to properly be set, - * we disable coexistence mode until we're done with - * obtaining an IP address. One exception is if we - * are currently connected to a headset, since disabling - * coexistence would interrupt that connection. - */ - modifiedBluetoothCoexistenceMode = true; - - // Disable the coexistence mode - WifiNative.setBluetoothCoexistenceModeCommand( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); - } - - powerMode = WifiNative.getPowerModeCommand(); - if (powerMode < 0) { - // Handle the case where supplicant driver does not support - // getPowerModeCommand. - powerMode = DRIVER_POWER_MODE_AUTO; - } - if (powerMode != DRIVER_POWER_MODE_ACTIVE) { - WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); - } - - Log.d(TAG, "DHCP request started"); - mDhcpThread = new Thread(new Runnable() { - public void run() { - if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { - Log.d(TAG, "DHCP request succeeded"); - sendMessage(CMD_IP_CONFIG_SUCCESS); - } else { - Log.d(TAG, "DHCP request failed: " + - NetworkUtils.getDhcpError()); - sendMessage(CMD_IP_CONFIG_FAILURE); - } - } - }); - mDhcpThread.start(); - } else { - if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { - Log.v(TAG, "Static IP configuration succeeded"); - sendMessage(CMD_IP_CONFIG_SUCCESS); - } else { - Log.v(TAG, "Static IP configuration failed"); - sendMessage(CMD_IP_CONFIG_FAILURE); - } - } - } - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - - switch(message.what) { - case CMD_IP_CONFIG_SUCCESS: - mReconnectCount = 0; - mLastSignalLevel = -1; // force update of signal strength - mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); - Log.d(TAG, "IP configuration: " + mDhcpInfo); - configureNetworkProperties(); - setDetailedState(DetailedState.CONNECTED); - sendNetworkStateChangeBroadcast(mLastBssid); - transitionTo(mConnectedState); - break; - case CMD_IP_CONFIG_FAILURE: - mWifiInfo.setIpAddress(0); - - Log.e(TAG, "IP configuration failed"); - /** - * If we've exceeded the maximum number of retries for DHCP - * to a given network, disable the network - */ - if (++mReconnectCount > getMaxDhcpRetries()) { - Log.e(TAG, "Failed " + - mReconnectCount + " times, Disabling " + mLastNetworkId); - WifiNative.disableNetworkCommand(mLastNetworkId); - } - - /* DHCP times out after about 30 seconds, we do a - * disconnect and an immediate reconnect to try again - */ - WifiNative.disconnectCommand(); - WifiNative.reconnectCommand(); - transitionTo(mDisconnectingState); - break; - case CMD_DISCONNECT: - if (message.arg2 == SYNCHRONOUS_CALL) { - SyncParams syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.disconnectCommand(); - } - transitionTo(mDisconnectingState); - break; - /* Ignore */ - case NETWORK_CONNECTION_EVENT: - break; - case CMD_STOP_DRIVER: - sendMessage(CMD_DISCONNECT); - deferMessage(message); - break; - case CMD_SET_SCAN_MODE: - if (message.arg1 == SCAN_ONLY_MODE) { - sendMessage(CMD_DISCONNECT); - deferMessage(message); - } - break; - case CMD_RECONFIGURE_IP: - deferMessage(message); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - - @Override - public void exit() { - /* reset power state & bluetooth coexistence if on DHCP */ - if (!mUseStaticIp) { - if (powerMode != DRIVER_POWER_MODE_ACTIVE) { - WifiNative.setPowerModeCommand(powerMode); - } - - if (modifiedBluetoothCoexistenceMode) { - // Set the coexistence mode back to its default value - WifiNative.setBluetoothCoexistenceModeCommand( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); - } - } - - } - } - - class ConnectedState extends HierarchicalState { - @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_DISCONNECT: - if (message.arg2 == SYNCHRONOUS_CALL) { - SyncParams syncParams = (SyncParams) message.obj; - syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); - notifyOnMsgObject(message); - } else { - /* asynchronous handling */ - WifiNative.disconnectCommand(); - } - transitionTo(mDisconnectingState); - break; - case CMD_RECONFIGURE_IP: - Log.d(TAG,"Reconfiguring IP on connection"); - NetworkUtils.resetConnections(mInterfaceName); - transitionTo(mConnectingState); - break; - case CMD_STOP_DRIVER: - sendMessage(CMD_DISCONNECT); - deferMessage(message); - break; - case CMD_SET_SCAN_MODE: - if (message.arg1 == SCAN_ONLY_MODE) { - sendMessage(CMD_DISCONNECT); - deferMessage(message); - } - break; - /* Ignore */ - case NETWORK_CONNECTION_EVENT: - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DisconnectingState extends HierarchicalState { - @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_STOP_DRIVER: /* Stop driver only after disconnect handled */ - deferMessage(message); - break; - case CMD_SET_SCAN_MODE: - if (message.arg1 == SCAN_ONLY_MODE) { - deferMessage(message); - } - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class DisconnectedState extends HierarchicalState { - @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_SET_SCAN_MODE: - if (message.arg1 == SCAN_ONLY_MODE) { - WifiNative.setScanResultHandlingCommand(message.arg1); - //Supplicant disconnect to prevent further connects - WifiNative.disconnectCommand(); - mIsScanMode = true; - transitionTo(mScanModeState); - } - break; - /* Ignore network disconnect */ - case NETWORK_DISCONNECTION_EVENT: - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - class SoftApStartedState extends HierarchicalState { - @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_STOP_AP: - Log.d(TAG,"Stopping Soft AP"); - setWifiApState(WIFI_AP_STATE_DISABLING); - try { - nwService.stopAccessPoint(); - } catch(Exception e) { - Log.e(TAG, "Exception in stopAccessPoint()"); - } - transitionTo(mDriverLoadedState); - break; - case CMD_START_AP: - Log.d(TAG,"SoftAP set on a running access point"); - try { - nwService.setAccessPoint((WifiConfiguration) message.obj, - mInterfaceName, - SOFTAP_IFACE); - } catch(Exception e) { - Log.e(TAG, "Exception in nwService during soft AP set"); - try { - nwService.stopAccessPoint(); - } catch (Exception ee) { - Slog.e(TAG, "Could not stop AP, :" + ee); - } - sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); - } - break; - /* Fail client mode operation when soft AP is enabled */ - case CMD_START_SUPPLICANT: - Log.e(TAG,"Cannot start supplicant with a running soft AP"); - setWifiState(WIFI_STATE_UNKNOWN); - break; - default: - return NOT_HANDLED; - } - EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); - return HANDLED; - } - } - - - class SupplicantStateTracker extends HierarchicalStateMachine { - - private int mRssiPollToken = 0; - - /** - * The max number of the WPA supplicant loop iterations before we - * decide that the loop should be terminated: - */ - private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4; - private int mLoopDetectIndex = 0; - private int mLoopDetectCount = 0; - - /** - * Supplicant state change commands follow - * the ordinal values defined in SupplicantState.java - */ - private static final int DISCONNECTED = 0; - private static final int INACTIVE = 1; - private static final int SCANNING = 2; - private static final int ASSOCIATING = 3; - private static final int ASSOCIATED = 4; - private static final int FOUR_WAY_HANDSHAKE = 5; - private static final int GROUP_HANDSHAKE = 6; - private static final int COMPLETED = 7; - private static final int DORMANT = 8; - private static final int UNINITIALIZED = 9; - private static final int INVALID = 10; - - private HierarchicalState mUninitializedState; - private HierarchicalState mInitializedState; - private HierarchicalState mInactiveState; - private HierarchicalState mDisconnectState; - private HierarchicalState mScanState; - private HierarchicalState mConnectState; - private HierarchicalState mHandshakeState; - private HierarchicalState mCompletedState; - private HierarchicalState mDormantState; - - - public SupplicantStateTracker(Context context, Handler target) { - super(TAG, target.getLooper()); - - mUninitializedState = new UninitializedState(); - mInitializedState = new InitializedState(); - mInactiveState = new InactiveState(); - mDisconnectState = new DisconnectedState(); - mScanState = new ScanState(); - mConnectState = new ConnectState(); - mHandshakeState = new HandshakeState(); - mCompletedState = new CompletedState(); - mDormantState = new DormantState(); - - - addState(mUninitializedState); - addState(mInitializedState); - addState(mInactiveState, mInitializedState); - addState(mDisconnectState, mInitializedState); - addState(mScanState, mInitializedState); - addState(mConnectState, mInitializedState); - addState(mHandshakeState, mConnectState); - addState(mCompletedState, mConnectState); - addState(mDormantState, mInitializedState); - - setInitialState(mUninitializedState); - - //start the state machine - start(); - } - - public void handleEvent(StateChangeResult stateChangeResult) { - SupplicantState newState = (SupplicantState) stateChangeResult.state; - - // Supplicant state change - // [31-13] Reserved for future use - // [8 - 0] Supplicant state (as defined in SupplicantState.java) - // 50023 supplicant_state_changed (custom|1|5) - EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal()); - - sendMessage(obtainMessage(newState.ordinal(), stateChangeResult)); - } - - public void resetSupplicantState() { - transitionTo(mUninitializedState); - } - - private void resetLoopDetection() { - mLoopDetectCount = 0; - mLoopDetectIndex = 0; - } - - private boolean handleTransition(Message msg) { - if (DBG) Log.d(TAG, getName() + msg.toString() + "\n"); - switch (msg.what) { - case DISCONNECTED: - transitionTo(mDisconnectState); - break; - case SCANNING: - transitionTo(mScanState); - break; - case ASSOCIATING: - StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; - /* BSSID is valid only in ASSOCIATING state */ - mWifiInfo.setBSSID(stateChangeResult.BSSID); - //$FALL-THROUGH$ - case ASSOCIATED: - case FOUR_WAY_HANDSHAKE: - case GROUP_HANDSHAKE: - transitionTo(mHandshakeState); - break; - case COMPLETED: - transitionTo(mCompletedState); - break; - case DORMANT: - transitionTo(mDormantState); - break; - case INACTIVE: - transitionTo(mInactiveState); - break; - case UNINITIALIZED: - case INVALID: - transitionTo(mUninitializedState); - break; - default: - return NOT_HANDLED; - } - StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; - SupplicantState supState = (SupplicantState) stateChangeResult.state; - setDetailedState(WifiInfo.getDetailedStateOf(supState)); - mWifiInfo.setSupplicantState(supState); - mWifiInfo.setNetworkId(stateChangeResult.networkId); - //TODO: Modify WifiMonitor to report SSID on events - //mWifiInfo.setSSID() - return HANDLED; - } - - /******************************************************** - * HSM states - *******************************************************/ - - class InitializedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - } - @Override - public boolean processMessage(Message message) { - if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); - switch (message.what) { - case CMD_START_SCAN: - WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); - break; - default: - if (DBG) Log.w(TAG, "Ignoring " + message); - break; - } - return HANDLED; - } - } - - class UninitializedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - mNetworkInfo.setIsAvailable(false); - resetLoopDetection(); - mPasswordKeyMayBeIncorrect = false; - } - @Override - public boolean processMessage(Message message) { - switch(message.what) { - default: - if (!handleTransition(message)) { - if (DBG) Log.w(TAG, "Ignoring " + message); - } - break; - } - return HANDLED; - } - } - - class InactiveState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(false); - resetLoopDetection(); - mPasswordKeyMayBeIncorrect = false; - - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - } - @Override - public boolean processMessage(Message message) { - return handleTransition(message); - } - } - - - class DisconnectedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(true); - resetLoopDetection(); - - /* If a disconnect event happens after a password key failure - * event, disable the network - */ - if (mPasswordKeyMayBeIncorrect) { - Log.d(TAG, "Failed to authenticate, disabling network " + - mWifiInfo.getNetworkId()); - WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId()); - mPasswordKeyMayBeIncorrect = false; - sendSupplicantStateChangedBroadcast(stateChangeResult, true); - } - else { - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - } - } - @Override - public boolean processMessage(Message message) { - return handleTransition(message); - } - } - - class ScanState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(true); - mPasswordKeyMayBeIncorrect = false; - resetLoopDetection(); - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - } - @Override - public boolean processMessage(Message message) { - return handleTransition(message); - } - } - - class ConnectState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - } - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_START_SCAN: - WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); - WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class HandshakeState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - final Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(true); - - if (mLoopDetectIndex > message.what) { - mLoopDetectCount++; - } - if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) { - WifiNative.disableNetworkCommand(stateChangeResult.networkId); - mLoopDetectCount = 0; - } - - mLoopDetectIndex = message.what; - - mPasswordKeyMayBeIncorrect = false; - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - } - @Override - public boolean processMessage(Message message) { - return handleTransition(message); - } - } - - class CompletedState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(true); - - mRssiPollToken++; - if (mEnableRssiPolling) { - sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), - POLL_RSSI_INTERVAL_MSECS); - } - - resetLoopDetection(); - - mPasswordKeyMayBeIncorrect = false; - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - } - @Override - public boolean processMessage(Message message) { - switch(message.what) { - case ASSOCIATING: - case ASSOCIATED: - case FOUR_WAY_HANDSHAKE: - case GROUP_HANDSHAKE: - case COMPLETED: - break; - case CMD_RSSI_POLL: - if (message.arg1 == mRssiPollToken) { - // Get Info and continue polling - requestPolledInfo(); - sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), - POLL_RSSI_INTERVAL_MSECS); - } else { - // Polling has completed - } - break; - case CMD_ENABLE_RSSI_POLL: - mRssiPollToken++; - if (mEnableRssiPolling) { - // first poll - requestPolledInfo(); - sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), - POLL_RSSI_INTERVAL_MSECS); - } - break; - default: - return handleTransition(message); - } - return HANDLED; - } - } - - class DormantState extends HierarchicalState { - @Override - public void enter() { - if (DBG) Log.d(TAG, getName() + "\n"); - Message message = getCurrentMessage(); - StateChangeResult stateChangeResult = (StateChangeResult) message.obj; - - mNetworkInfo.setIsAvailable(true); - resetLoopDetection(); - mPasswordKeyMayBeIncorrect = false; - - sendSupplicantStateChangedBroadcast(stateChangeResult, false); - - /* TODO: reconnect is now being handled at DHCP failure handling - * If we run into issues with staying in Dormant state, might - * need a reconnect here - */ - } - @Override - public boolean processMessage(Message message) { - return handleTransition(message); - } - } - } - - private class NotificationEnabledSettingObserver extends ContentObserver { - - public NotificationEnabledSettingObserver(Handler handler) { - super(handler); - } - - public void register() { - ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); - mNotificationEnabled = getValue(); - } - - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - - mNotificationEnabled = getValue(); - if (!mNotificationEnabled) { - // Remove any notification that may be showing - setNotificationVisible(false, 0, true, 0); - } - - resetNotificationTimer(); - } - - private boolean getValue() { - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; - } - } -} +}
\ No newline at end of file |