diff options
34 files changed, 413 insertions, 156 deletions
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 43a163d..6382cee 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -858,6 +858,9 @@ public class SearchManager */ public Intent getAssistIntent(Context context, int userHandle) { try { + if (mService == null) { + return null; + } ComponentName comp = mService.getAssistIntent(userHandle); if (comp == null) { return null; diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 0138b1c..2fd52b8 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -107,6 +107,15 @@ public final class WifiDisplay implements Parcelable { && Objects.equal(mDeviceAlias, other.mDeviceAlias); } + /** + * Returns true if the other display is not null and has the same address as this one. + * Can be used to perform identity comparisons on displays ignoring properties + * that might change during a connection such as the name or alias. + */ + public boolean hasSameAddress(WifiDisplay other) { + return other != null && mDeviceAddress.equals(other.mDeviceAddress); + } + @Override public int hashCode() { // The address on its own should be sufficiently unique for hashing purposes. diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index f07002e..6f1cc94 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -930,11 +930,13 @@ public class InputMethodService extends AbstractInputMethodService { */ public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) { - if (isFullscreen) { - mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); - } else { - mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); + final int currentHeight = mWindow.getWindow().getAttributes().height; + final int newHeight = isFullscreen ? MATCH_PARENT : WRAP_CONTENT; + if (mIsInputViewShown && currentHeight != newHeight) { + Log.w(TAG, "Window size has been changed. This may cause jankiness of resizing window: " + + currentHeight + " -> " + newHeight); } + mWindow.getWindow().setLayout(MATCH_PARENT, newHeight); } /** @@ -997,10 +999,11 @@ public class InputMethodService extends AbstractInputMethodService { } void updateExtractFrameVisibility() { - int vis; + final int vis; if (isFullscreenMode()) { vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE; - mExtractFrame.setVisibility(View.VISIBLE); + // "vis" should be applied for the extract frame as well in the fullscreen mode. + mExtractFrame.setVisibility(vis); } else { vis = View.VISIBLE; mExtractFrame.setVisibility(View.GONE); diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java index 874e80a..8dc900e 100644 --- a/core/java/android/net/DhcpStateMachine.java +++ b/core/java/android/net/DhcpStateMachine.java @@ -351,6 +351,8 @@ public class DhcpStateMachine extends StateMachine { DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); if (dhcpAction == DhcpAction.START) { + /* Stop any existing DHCP daemon before starting new */ + NetworkUtils.stopDhcp(mInterfaceName); if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName); success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal); mDhcpInfo = dhcpInfoInternal; diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index ed51818..0ca9183 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -44,6 +44,7 @@ public final class Trace { public static final long TRACE_TAG_AUDIO = 1L << 8; public static final long TRACE_TAG_VIDEO = 1L << 9; public static final long TRACE_TAG_CAMERA = 1L << 10; + private static final long TRACE_TAG_NOT_READY = 1L << 63; public static final int TRACE_FLAGS_START_BIT = 1; public static final String[] TRACE_TAGS = { @@ -53,11 +54,8 @@ public final class Trace { public static final String PROPERTY_TRACE_TAG_ENABLEFLAGS = "debug.atrace.tags.enableflags"; - // This works as a "not ready" flag because TRACE_TAG_ALWAYS is always set. - private static final long TRACE_FLAGS_NOT_READY = 0; - // Must be volatile to avoid word tearing. - private static volatile long sEnabledTags = TRACE_FLAGS_NOT_READY; + private static volatile long sEnabledTags = TRACE_TAG_NOT_READY; private static native long nativeGetEnabledTags(); private static native void nativeTraceCounter(long tag, String name, int value); @@ -99,7 +97,7 @@ public final class Trace { */ private static long cacheEnabledTags() { long tags = nativeGetEnabledTags(); - if (tags == TRACE_FLAGS_NOT_READY) { + if (tags == TRACE_TAG_NOT_READY) { Log.w(TAG, "Unexpected value from nativeGetEnabledTags: " + tags); // keep going } @@ -115,7 +113,7 @@ public final class Trace { */ public static boolean isTagEnabled(long traceTag) { long tags = sEnabledTags; - if (tags == TRACE_FLAGS_NOT_READY) { + if (tags == TRACE_TAG_NOT_READY) { tags = cacheEnabledTags(); } return (tags & traceTag) != 0; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b1a44c5..521e686 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -291,6 +291,7 @@ public class Editor { mErrorWasChanged = true; if (mError == null) { + setErrorIcon(null); if (mErrorPopup != null) { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); @@ -299,10 +300,11 @@ public class Editor { mErrorPopup = null; } - setErrorIcon(null); - } else if (mTextView.isFocused()) { - showError(); + } else { setErrorIcon(icon); + if (mTextView.isFocused()) { + showError(); + } } } @@ -321,8 +323,6 @@ public class Editor { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); } - - setErrorIcon(null); } mShowErrorAfterAttach = false; diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index e7c4c23..9537ac4 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -122,9 +122,9 @@ static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0); } -static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject) +static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject, jboolean p2pSupported) { - return (jboolean)(::wifi_stop_supplicant() == 0); + return (jboolean)(::wifi_stop_supplicant(p2pSupported) == 0); } static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject, jstring jIface) @@ -204,7 +204,7 @@ static JNINativeMethod gWifiMethods[] = { { "isDriverLoaded", "()Z", (void *)android_net_wifi_isDriverLoaded }, { "unloadDriver", "()Z", (void *)android_net_wifi_unloadDriver }, { "startSupplicant", "(Z)Z", (void *)android_net_wifi_startSupplicant }, - { "killSupplicant", "()Z", (void *)android_net_wifi_killSupplicant }, + { "killSupplicant", "(Z)Z", (void *)android_net_wifi_killSupplicant }, { "connectToSupplicant", "(Ljava/lang/String;)Z", (void *)android_net_wifi_connectToSupplicant }, { "closeSupplicantConnection", "(Ljava/lang/String;)V", diff --git a/core/res/res/drawable-hdpi/ic_coins_l.png b/core/res/res/drawable-hdpi/ic_coins_l.png Binary files differnew file mode 100644 index 0000000..e1e3e2a --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_coins_l.png diff --git a/core/res/res/drawable-mdpi/ic_coins_l.png b/core/res/res/drawable-mdpi/ic_coins_l.png Binary files differnew file mode 100644 index 0000000..a6d7abb --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_coins_l.png diff --git a/core/res/res/drawable-xhdpi/ic_coins_l.png b/core/res/res/drawable-xhdpi/ic_coins_l.png Binary files differnew file mode 100644 index 0000000..84e7e72 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_coins_l.png diff --git a/core/res/res/layout/sms_short_code_confirmation_dialog.xml b/core/res/res/layout/sms_short_code_confirmation_dialog.xml index ec39d97..d82f560 100644 --- a/core/res/res/layout/sms_short_code_confirmation_dialog.xml +++ b/core/res/res/layout/sms_short_code_confirmation_dialog.xml @@ -30,9 +30,9 @@ style="?android:attr/textAppearanceMedium" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingLeft="16dip" - android:paddingRight="16dip" - android:paddingTop="8dip" + android:paddingLeft="20dip" + android:paddingRight="20dip" + android:paddingTop="16dip" android:paddingBottom="16dip" /> <TableLayout android:id="@+id/sms_short_code_detail_layout" @@ -51,7 +51,7 @@ android:layout_height="wrap_content" android:paddingLeft="8dip" android:paddingRight="8dip" - android:src="@null" /> + android:src="@drawable/ic_coins_l" /> <TextView android:id="@+id/sms_short_code_detail_message" android:layout_width="match_parent" android:layout_height="wrap_content" /> @@ -60,14 +60,19 @@ <TableRow android:layout_width="wrap_content" android:layout_height="wrap_content" > - + <RelativeLayout android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="12dip" + android:paddingLeft="8dip" > <CheckBox android:id="@+id/sms_short_code_remember_choice_checkbox" + android:paddingTop="11dip" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingRight="8dip" /> + android:layout_height="wrap_content" /> + </RelativeLayout> <TextView android:id="@+id/sms_short_code_remember_choice_text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingTop="18dip" android:text="@string/sms_short_code_remember_choice" /> </TableRow> @@ -77,6 +82,7 @@ <Space android:layout_gravity="fill" /> <TextView android:id="@+id/sms_short_code_remember_undo_instruction" + android:paddingTop="10dip" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </TableRow> diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png Binary files differnew file mode 100644 index 0000000..e3f3144 --- /dev/null +++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3b7d73a..ea28a51 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1000,4 +1000,25 @@ provisioning on some carriers, working around a bug (7305641) where if the preferred is used we don't try the others. --> <bool name="config_dontPreferApn">false</bool> + + <!-- Vibrator pattern to be used as the default for notifications + that specify DEFAULT_VIBRATE. + --> + <integer-array name="config_defaultNotificationVibePattern"> + <item>0</item> + <item>150</item> + <item>200</item> + <item>250</item> + </integer-array> + + <!-- Vibrator pattern to be used as the default for notifications + that do not specify vibration but vibrate anyway because the device + is in vibrate mode. + --> + <integer-array name="config_notificationFallbackVibePattern"> + <item>0</item> + <item>33</item> + <item>150</item> + <item>50</item> + </integer-array> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 68a0289..6858732 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1525,6 +1525,8 @@ <java-symbol type="array" name="radioAttributes" /> <java-symbol type="array" name="config_oemUsbModeOverride" /> <java-symbol type="array" name="config_locationProviderPackageNames" /> + <java-symbol type="array" name="config_defaultNotificationVibePattern" /> + <java-symbol type="array" name="config_notificationFallbackVibePattern" /> <java-symbol type="bool" name="config_animateScreenLights" /> <java-symbol type="bool" name="config_automatic_brightness_available" /> <java-symbol type="bool" name="config_sf_limitedAlpha" /> diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 315196e..22f699f 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -436,6 +436,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { private boolean mDockAudioMediaEnabled = true; + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// @@ -3324,6 +3326,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP); } + + synchronized (mSettingsLock) { + AudioSystem.setForceUse(AudioSystem.FOR_DOCK, + mDockAudioMediaEnabled ? + AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE); + } + // indicate the end of reconfiguration phase to audio HAL AudioSystem.setParameters("restarting=false"); break; @@ -3751,13 +3760,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { config = AudioSystem.FORCE_BT_CAR_DOCK; break; case Intent.EXTRA_DOCK_STATE_LE_DESK: - synchronized (mSettingsLock) { - if (mDockAudioMediaEnabled) { - config = AudioSystem.FORCE_ANALOG_DOCK; - } else { - config = AudioSystem.FORCE_NONE; - } - } + config = AudioSystem.FORCE_ANALOG_DOCK; break; case Intent.EXTRA_DOCK_STATE_HE_DESK: config = AudioSystem.FORCE_DIGITAL_DOCK; @@ -3766,8 +3769,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished { default: config = AudioSystem.FORCE_NONE; } - - AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); + // Low end docks have a menu to enable or disable audio + // (see mDockAudioMediaEnabled) + if (!((dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) || + ((dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) && + (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK)))) { + AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); + } + mDockState = dockState; } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 4414191..169502b 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -50,7 +50,7 @@ import java.util.Map; * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr> * <tr><td>{@link #KEY_CHANNEL_COUNT}</td><td>Integer</td><td></td></tr> * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr> - * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if content is AAC audio, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr> + * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr> * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr> * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>A mask of audio channel assignments</td></tr> * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr> @@ -140,6 +140,8 @@ public final class MediaFormat { * A key mapping to a value of 1 if the content is AAC audio and * audio frames are prefixed with an ADTS header. * The associated value is an integer (0 or 1). + * This key is only supported when _decoding_ content, it cannot + * be used to configure an encoder to emit ADTS output. */ public static final String KEY_IS_ADTS = "is-adts"; diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 2a5a16e..8701f36 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -862,7 +862,7 @@ public class MediaRouter { private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) { for (int i = 0; i < displays.length; i++) { final WifiDisplay other = displays[i]; - if (d.getDeviceAddress().equals(other.getDeviceAddress())) { + if (d.hasSameAddress(other)) { return other; } } diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml index 2669c7e..b1104cc 100644 --- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml @@ -141,7 +141,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="128dp" android:id="@+id/search_light" android:layout_height="match_parent" @@ -282,7 +282,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="162dp" android:id="@+id/search_light" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 440a4e1..da52d89 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -145,7 +145,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:layout_width="80dp" android:id="@+id/search_light" android:layout_height="match_parent" @@ -289,7 +289,7 @@ /> </LinearLayout> - <ImageView + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/search_light" android:layout_height="80dp" android:layout_width="match_parent" diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index 76ba811..4e8aba7 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -129,12 +129,19 @@ public class KeyguardViewManager { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (mKeyguardHost.getVisibility() == View.VISIBLE) { - // only propagate configuration messages if we're currently showing - maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); - } else { - if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); - } + post(new Runnable() { + @Override + public void run() { + synchronized (KeyguardViewManager.this) { + if (mKeyguardHost.getVisibility() == View.VISIBLE) { + // only propagate configuration messages if we're currently showing + maybeCreateKeyguardLocked(shouldEnableScreenRotation(), true, null); + } else { + if (DEBUG) Log.v(TAG, "onConfigurationChanged: view not visible"); + } + } + } + }); } @Override diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index df4c661..c227619 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -22,6 +22,7 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.SearchManager; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -166,6 +167,9 @@ public class KeyguardViewMediator { /** UserManager for querying number of users */ private UserManager mUserManager; + /** SearchManager for determining whether or not search assistant is available */ + private SearchManager mSearchManager; + /** * Used to keep the device awake while to ensure the keyguard finishes opening before * we sleep. @@ -527,6 +531,7 @@ public class KeyguardViewMediator { * Let us know that the system is ready after startup. */ public void onSystemReady() { + mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); synchronized (this) { if (DEBUG) Log.d(TAG, "onSystemReady"); mSystemReady = true; @@ -1313,6 +1318,9 @@ public class KeyguardViewMediator { // showing secure lockscreen; disable ticker. flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER; } + if (!isAssistantAvailable()) { + flags |= StatusBarManager.DISABLE_SEARCH; + } } if (DEBUG) { @@ -1410,4 +1418,8 @@ public class KeyguardViewMediator { mKeyguardViewManager.showAssistant(); } + private boolean isAssistantAvailable() { + return mSearchManager != null + && mSearchManager.getAssistIntent(mContext, UserHandle.USER_CURRENT) != null; + } } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index ad1dfb2..a7c4d73 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -2686,18 +2686,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { state + "/" + info.getDetailedState()); } - // Connectivity state changed: - // [31-14] Reserved for future use - // [13-10] Network subtype (for mobile network, as defined - // by TelephonyManager) - // [9-4] Detailed state ordinal (as defined by - // NetworkInfo.DetailedState) - // [3-0] Network type (as defined by ConnectivityManager) - int eventLogParam = (info.getType() & 0xf) | - ((info.getDetailedState().ordinal() & 0x3f) << 4) | - (info.getSubtype() << 10); - EventLog.writeEvent(EventLogTags.CONNECTIVITY_STATE_CHANGED, - eventLogParam); + EventLogTags.writeConnectivityStateChanged( + info.getType(), info.getSubtype(), info.getDetailedState().ordinal()); if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) { diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index 0fe66fc..8bc2da2 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -135,12 +135,8 @@ option java_package com.android.server # --------------------------- # ConnectivityService.java # --------------------------- -# Connectivity state changed: -# [31-14] Reserved for future use -# [13-10] Network subtype (for mobile network, as defined by TelephonyManager) -# [ 9- 4] Detailed state ordinal (as defined by NetworkInfo.DetailedState) -# [ 3- 0] Network type (as defined by ConnectivityManager) -50020 connectivity_state_changed (custom|1|5) +# Connectivity state changed +50020 connectivity_state_changed (type|1),(subtype|1),(state|1) # --------------------------- diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 89fa6d0..7a55497c 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -506,7 +506,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } else { Intent statusChanged = new Intent(); - statusChanged.putExtras(extras); + statusChanged.putExtras(new Bundle(extras)); statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status); try { synchronized (this) { @@ -541,7 +541,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } else { Intent locationChanged = new Intent(); - locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location); + locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, new Location(location)); try { synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index f3a38f0..fa84f48 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -101,6 +101,7 @@ public class NotificationManagerService extends INotificationManager.Stub private static final int SHORT_DELAY = 2000; // 2 seconds private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; + private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; private static final boolean SCORE_ONGOING_HIGHER = false; @@ -125,6 +126,9 @@ public class NotificationManagerService extends INotificationManager.Stub private int mDefaultNotificationLedOn; private int mDefaultNotificationLedOff; + private long[] mDefaultVibrationPattern; + private long[] mFallbackVibrationPattern; + private boolean mSystemReady; private int mDisabledNotifications; @@ -596,6 +600,19 @@ public class NotificationManagerService extends INotificationManager.Stub } } + static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) { + int[] ar = r.getIntArray(resid); + if (ar == null) { + return def; + } + final int len = ar.length > maxlen ? maxlen : ar.length; + long[] out = new long[len]; + for (int i=0; i<len; i++) { + out[i] = ar[i]; + } + return out; + } + NotificationManagerService(Context context, StatusBarManagerService statusBar, LightsService lights) { @@ -622,6 +639,16 @@ public class NotificationManagerService extends INotificationManager.Stub mDefaultNotificationLedOff = resources.getInteger( com.android.internal.R.integer.config_defaultNotificationLedOff); + mDefaultVibrationPattern = getLongArray(resources, + com.android.internal.R.array.config_defaultNotificationVibePattern, + VIBRATE_PATTERN_MAXLEN, + DEFAULT_VIBRATE_PATTERN); + + mFallbackVibrationPattern = getLongArray(resources, + com.android.internal.R.array.config_notificationFallbackVibePattern, + VIBRATE_PATTERN_MAXLEN, + DEFAULT_VIBRATE_PATTERN); + // Don't start allowing notifications until the setup wizard has run once. // After that, including subsequent boots, init with notifications turned on. // This works on the first boot because the setup wizard will toggle this @@ -1050,16 +1077,27 @@ public class NotificationManagerService extends INotificationManager.Stub final AudioManager audioManager = (AudioManager) mContext .getSystemService(Context.AUDIO_SERVICE); + // sound final boolean useDefaultSound = (notification.defaults & Notification.DEFAULT_SOUND) != 0; - if (useDefaultSound || notification.sound != null) { - Uri uri; - if (useDefaultSound) { - uri = Settings.System.DEFAULT_NOTIFICATION_URI; - } else { - uri = notification.sound; - } + + Uri soundUri = null; + boolean hasValidSound = false; + + if (useDefaultSound) { + soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; + + // check to see if the default notification sound is silent + ContentResolver resolver = mContext.getContentResolver(); + hasValidSound = Settings.System.getString(resolver, + Settings.System.NOTIFICATION_SOUND) != null; + } else if (notification.sound != null) { + soundUri = notification.sound; + hasValidSound = (soundUri != null); + } + + if (hasValidSound) { boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; int audioStreamType; if (notification.audioStreamType >= 0) { @@ -1076,7 +1114,7 @@ public class NotificationManagerService extends INotificationManager.Stub try { final IRingtonePlayer player = mAudioService.getRingtonePlayer(); if (player != null) { - player.playAsync(uri, user, looping, audioStreamType); + player.playAsync(soundUri, user, looping, audioStreamType); } } catch (RemoteException e) { } finally { @@ -1086,24 +1124,40 @@ public class NotificationManagerService extends INotificationManager.Stub } // vibrate + // Does the notification want to specify its own vibration? + final boolean hasCustomVibrate = notification.vibrate != null; + // new in 4.2: if there was supposed to be a sound and we're in vibrate mode, - // we always vibrate, even if no vibration was specified + // and no other vibration is specified, we apply the default vibration anyway final boolean convertSoundToVibration = - notification.vibrate == null - && (useDefaultSound || notification.sound != null) + !hasCustomVibrate + && hasValidSound && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); + // The DEFAULT_VIBRATE flag trumps any custom vibration. final boolean useDefaultVibrate = - (notification.defaults & Notification.DEFAULT_VIBRATE) != 0 - || convertSoundToVibration; + (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; - if ((useDefaultVibrate || notification.vibrate != null) + if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) && !(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { mVibrateNotification = r; - mVibrator.vibrate(useDefaultVibrate ? DEFAULT_VIBRATE_PATTERN - : notification.vibrate, - ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); + if (useDefaultVibrate || convertSoundToVibration) { + // Escalate privileges so we can use the vibrator even if the notifying app + // does not have the VIBRATE permission. + long identity = Binder.clearCallingIdentity(); + try { + mVibrator.vibrate(convertSoundToVibration ? mFallbackVibrationPattern + : mDefaultVibrationPattern, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else if (notification.vibrate.length > 1) { + // If you want your own vibration pattern, you need the VIBRATE permission + mVibrator.vibrate(notification.vibrate, + ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1); + } } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index d2cd646..db64a9a 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -14229,6 +14229,7 @@ public final class ActivityManagerService extends ActivityManagerNative startHomeActivityLocked(userId); } + EventLogTags.writeAmSwitchUser(userId); getUserManagerLocked().userForeground(userId); sendUserSwitchBroadcastsLocked(oldUserId, userId); if (needStart) { diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index 6ee7507..88c0c03 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -83,3 +83,6 @@ option java_package com.android.server.am 30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5) # Log.wtf() called 30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3) + +# User switched +30041 am_switch_user (id|1|5) diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java index 3a6e1a6..105c253 100644 --- a/services/java/com/android/server/display/PersistentDataStore.java +++ b/services/java/com/android/server/display/PersistentDataStore.java @@ -81,6 +81,15 @@ final class PersistentDataStore { } } + public WifiDisplay getRememberedWifiDisplay(String deviceAddress) { + loadIfNeeded(); + int index = findRememberedWifiDisplay(deviceAddress); + if (index >= 0) { + return mRememberedWifiDisplays.get(index); + } + return null; + } + public WifiDisplay[] getRememberedWifiDisplays() { loadIfNeeded(); return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]); @@ -137,22 +146,6 @@ final class PersistentDataStore { return true; } - public boolean renameWifiDisplay(String deviceAddress, String alias) { - int index = findRememberedWifiDisplay(deviceAddress); - if (index >= 0) { - WifiDisplay display = mRememberedWifiDisplays.get(index); - if (Objects.equal(display.getDeviceAlias(), alias)) { - return false; // already has this alias - } - WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress, - display.getDeviceName(), alias); - mRememberedWifiDisplays.set(index, renamedDisplay); - setDirty(); - return true; - } - return false; - } - public boolean forgetWifiDisplay(String deviceAddress) { int index = findRememberedWifiDisplay(deviceAddress); if (index >= 0) { diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index 45fff30..c8a44d2 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -45,6 +45,8 @@ import android.view.Surface; import java.io.PrintWriter; import java.util.Arrays; +import libcore.util.Objects; + /** * Connects to Wifi displays that implement the Miracast protocol. * <p> @@ -224,16 +226,18 @@ final class WifiDisplayAdapter extends DisplayAdapter { } } - if (mPersistentDataStore.renameWifiDisplay(address, alias)) { - mPersistentDataStore.saveIfNeeded(); - updateRememberedDisplaysLocked(); - scheduleStatusChangedBroadcastLocked(); + WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address); + if (display != null && !Objects.equal(display.getDeviceAlias(), alias)) { + display = new WifiDisplay(address, display.getDeviceName(), alias); + if (mPersistentDataStore.rememberWifiDisplay(display)) { + mPersistentDataStore.saveIfNeeded(); + updateRememberedDisplaysLocked(); + scheduleStatusChangedBroadcastLocked(); + } } - if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address) - && mDisplayDevice != null) { - mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName()); - sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); + if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { + renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName()); } } @@ -272,9 +276,42 @@ final class WifiDisplayAdapter extends DisplayAdapter { mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); } - private void handleConnectLocked(WifiDisplay display, + private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() { + // It may happen that a display name has changed since it was remembered. + // Consult the list of available displays and update the name if needed. + // We don't do anything special for the active display here. The display + // controller will send a separate event when it needs to be updates. + boolean changed = false; + for (int i = 0; i < mRememberedDisplays.length; i++) { + WifiDisplay rememberedDisplay = mRememberedDisplays[i]; + WifiDisplay availableDisplay = findAvailableDisplayLocked( + rememberedDisplay.getDeviceAddress()); + if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) { + if (DEBUG) { + Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: " + + "updating remembered display to " + availableDisplay); + } + mRememberedDisplays[i] = availableDisplay; + changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay); + } + } + if (changed) { + mPersistentDataStore.saveIfNeeded(); + } + } + + private WifiDisplay findAvailableDisplayLocked(String address) { + for (WifiDisplay display : mAvailableDisplays) { + if (display.getDeviceAddress().equals(address)) { + return display; + } + } + return null; + } + + private void addDisplayDeviceLocked(WifiDisplay display, Surface surface, int width, int height, int flags) { - handleDisconnectLocked(); + removeDisplayDeviceLocked(); if (mPersistentDataStore.rememberWifiDisplay(display)) { mPersistentDataStore.saveIfNeeded(); @@ -303,7 +340,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { scheduleUpdateNotificationLocked(); } - private void handleDisconnectLocked() { + private void removeDisplayDeviceLocked() { if (mDisplayDevice != null) { mDisplayDevice.clearSurfaceLocked(); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); @@ -313,6 +350,13 @@ final class WifiDisplayAdapter extends DisplayAdapter { } } + private void renameDisplayDeviceLocked(String name) { + if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) { + mDisplayDevice.setNameLocked(name); + sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); + } + } + private void scheduleStatusChangedBroadcastLocked() { mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { @@ -446,6 +490,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { || !Arrays.equals(mAvailableDisplays, availableDisplays)) { mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; mAvailableDisplays = availableDisplays; + fixRememberedDisplayNamesFromAvailableDisplaysLocked(); scheduleStatusChangedBroadcastLocked(); } } @@ -483,7 +528,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { int width, int height, int flags) { synchronized (getSyncRoot()) { display = mPersistentDataStore.applyWifiDisplayAlias(display); - handleConnectLocked(display, surface, width, height, flags); + addDisplayDeviceLocked(display, surface, width, height, flags); if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED || mActiveDisplay == null @@ -496,10 +541,24 @@ final class WifiDisplayAdapter extends DisplayAdapter { } @Override + public void onDisplayChanged(WifiDisplay display) { + synchronized (getSyncRoot()) { + display = mPersistentDataStore.applyWifiDisplayAlias(display); + if (mActiveDisplay != null + && mActiveDisplay.hasSameAddress(display) + && !mActiveDisplay.equals(display)) { + mActiveDisplay = display; + renameDisplayDeviceLocked(display.getFriendlyDisplayName()); + scheduleStatusChangedBroadcastLocked(); + } + } + } + + @Override public void onDisplayDisconnected() { // Stop listening. synchronized (getSyncRoot()) { - handleDisconnectLocked(); + removeDisplayDeviceLocked(); if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED || mActiveDisplay != null) { diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java index 39d042f..886e049 100644 --- a/services/java/com/android/server/display/WifiDisplayController.java +++ b/services/java/com/android/server/display/WifiDisplayController.java @@ -120,6 +120,12 @@ final class WifiDisplayController implements DumpUtils.Dump { // or are not trying to connect. private WifiP2pDevice mConnectingDevice; + // The device from which we are currently disconnecting. + private WifiP2pDevice mDisconnectingDevice; + + // The device to which we were previously trying to connect and are now canceling. + private WifiP2pDevice mCancelingDevice; + // The device to which we are currently connected, which means we have an active P2P group. private WifiP2pDevice mConnectedDevice; @@ -186,6 +192,7 @@ final class WifiDisplayController implements DumpUtils.Dump { updateWfdEnableState(); } + @Override public void dump(PrintWriter pw) { pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting); pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled); @@ -196,6 +203,8 @@ final class WifiDisplayController implements DumpUtils.Dump { pw.println("mDiscoverPeersRetriesLeft=" + mDiscoverPeersRetriesLeft); pw.println("mDesiredDevice=" + describeWifiP2pDevice(mDesiredDevice)); pw.println("mConnectingDisplay=" + describeWifiP2pDevice(mConnectingDevice)); + pw.println("mDisconnectingDisplay=" + describeWifiP2pDevice(mDisconnectingDevice)); + pw.println("mCancelingDisplay=" + describeWifiP2pDevice(mCancelingDevice)); pw.println("mConnectedDevice=" + describeWifiP2pDevice(mConnectedDevice)); pw.println("mConnectionRetriesLeft=" + mConnectionRetriesLeft); pw.println("mRemoteDisplay=" + mRemoteDisplay); @@ -384,7 +393,9 @@ final class WifiDisplayController implements DumpUtils.Dump { final int count = mAvailableWifiDisplayPeers.size(); final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count); for (int i = 0; i < count; i++) { - displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i)); + WifiP2pDevice device = mAvailableWifiDisplayPeers.get(i); + displays[i] = createWifiDisplay(device); + updateDesiredDevice(device); } mHandler.post(new Runnable() { @@ -395,6 +406,23 @@ final class WifiDisplayController implements DumpUtils.Dump { }); } + private void updateDesiredDevice(WifiP2pDevice device) { + // Handle the case where the device to which we are connecting or connected + // may have been renamed or reported different properties in the latest scan. + final String address = device.deviceAddress; + if (mDesiredDevice != null && mDesiredDevice.deviceAddress.equals(address)) { + if (DEBUG) { + Slog.d(TAG, "updateDesiredDevice: new information " + + describeWifiP2pDevice(device)); + } + mDesiredDevice.update(device); + if (mAdvertisedDisplay != null + && mAdvertisedDisplay.getDeviceAddress().equals(address)) { + readvertiseDisplay(createWifiDisplay(mDesiredDevice)); + } + } + } + private void connect(final WifiP2pDevice device) { if (mDesiredDevice != null && !mDesiredDevice.deviceAddress.equals(device.deviceAddress)) { @@ -459,12 +487,17 @@ final class WifiDisplayController implements DumpUtils.Dump { } // Step 2. Before we try to connect to a new device, disconnect from the old one. + if (mDisconnectingDevice != null) { + return; // wait for asynchronous callback + } if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) { Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName); + mDisconnectingDevice = mConnectedDevice; + mConnectedDevice = null; unadvertiseDisplay(); - final WifiP2pDevice oldDevice = mConnectedDevice; + final WifiP2pDevice oldDevice = mDisconnectingDevice; mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() { @Override public void onSuccess() { @@ -480,8 +513,8 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void next() { - if (mConnectedDevice == oldDevice) { - mConnectedDevice = null; + if (mDisconnectingDevice == oldDevice) { + mDisconnectingDevice = null; updateConnection(); } } @@ -491,13 +524,18 @@ final class WifiDisplayController implements DumpUtils.Dump { // Step 3. Before we try to connect to a new device, stop trying to connect // to the old one. + if (mCancelingDevice != null) { + return; // wait for asynchronous callback + } if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) { Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName); + mCancelingDevice = mConnectingDevice; + mConnectingDevice = null; unadvertiseDisplay(); mHandler.removeCallbacks(mConnectionTimeout); - final WifiP2pDevice oldDevice = mConnectingDevice; + final WifiP2pDevice oldDevice = mCancelingDevice; mWifiP2pManager.cancelConnect(mWifiP2pChannel, new ActionListener() { @Override public void onSuccess() { @@ -513,8 +551,8 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void next() { - if (mConnectingDevice == oldDevice) { - mConnectingDevice = null; + if (mCancelingDevice == oldDevice) { + mCancelingDevice = null; updateConnection(); } } @@ -763,13 +801,17 @@ final class WifiDisplayController implements DumpUtils.Dump { public void run() { if (oldSurface != null && surface != oldSurface) { mListener.onDisplayDisconnected(); - } else if (oldDisplay != null && !Objects.equal(display, oldDisplay)) { + } else if (oldDisplay != null && !oldDisplay.hasSameAddress(display)) { mListener.onDisplayConnectionFailed(); } if (display != null) { - if (!Objects.equal(display, oldDisplay)) { + if (!display.hasSameAddress(oldDisplay)) { mListener.onDisplayConnecting(display); + } else if (!display.equals(oldDisplay)) { + // The address is the same but some other property such as the + // name must have changed. + mListener.onDisplayChanged(display); } if (surface != null && surface != oldSurface) { mListener.onDisplayConnected(display, surface, width, height, flags); @@ -784,6 +826,12 @@ final class WifiDisplayController implements DumpUtils.Dump { advertiseDisplay(null, null, 0, 0, 0); } + private void readvertiseDisplay(WifiDisplay display) { + advertiseDisplay(display, mAdvertisedDisplaySurface, + mAdvertisedDisplayWidth, mAdvertisedDisplayHeight, + mAdvertisedDisplayFlags); + } + private static Inet4Address getInterfaceAddress(WifiP2pGroup info) { NetworkInterface iface; try { @@ -885,6 +933,7 @@ final class WifiDisplayController implements DumpUtils.Dump { void onDisplayConnecting(WifiDisplay display); void onDisplayConnectionFailed(); + void onDisplayChanged(WifiDisplay display); void onDisplayConnected(WifiDisplay display, Surface surface, int width, int height, int flags); void onDisplayDisconnected(); diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index e05442b..66c3ce9 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -16,8 +16,7 @@ package com.android.server.pm; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import android.app.Activity; import android.app.ActivityManager; @@ -26,7 +25,6 @@ import android.app.IStopUserCallback; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.graphics.Bitmap; @@ -34,6 +32,7 @@ import android.graphics.BitmapFactory; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; +import android.os.Handler; import android.os.IUserManager; import android.os.Process; import android.os.RemoteException; @@ -42,9 +41,17 @@ import android.os.UserManager; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastXmlSerializer; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; @@ -54,13 +61,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; @@ -95,19 +97,24 @@ public class UserManagerService extends IUserManager.Stub { private final Object mInstallLock; private final Object mPackagesLock; + private final Handler mHandler; + private final File mUsersDir; private final File mUserListFile; private final File mBaseUserPath; - private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); - private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>(); + private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); + + /** + * Set of user IDs being actively removed. Removed IDs linger in this set + * for several seconds to work around a VFS caching issue. + */ + // @GuardedBy("mPackagesLock") + private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray(); private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; - // This resets on a reboot. Otherwise it keeps incrementing so that user ids are - // not reused in quick succession - private int mNextUserId = MIN_USER_ID; private int mUserVersion = 0; private static UserManagerService sInstance; @@ -147,6 +154,7 @@ public class UserManagerService extends IUserManager.Stub { mPm = pm; mInstallLock = installLock; mPackagesLock = packagesLock; + mHandler = new Handler(); synchronized (mInstallLock) { synchronized (mPackagesLock) { mUsersDir = new File(dataDir, USER_INFO_DIR); @@ -190,7 +198,7 @@ public class UserManagerService extends IUserManager.Stub { if (ui.partial) { continue; } - if (!excludeDying || !mRemovingUserIds.contains(ui.id)) { + if (!excludeDying || !mRemovingUserIds.get(ui.id)) { users.add(ui); } } @@ -212,7 +220,7 @@ public class UserManagerService extends IUserManager.Stub { private UserInfo getUserInfoLocked(int userId) { UserInfo ui = mUsers.get(userId); // If it is partial and not in the process of being removed, return as unknown user. - if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) { + if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } @@ -502,7 +510,7 @@ public class UserManagerService extends IUserManager.Stub { private void fallbackToSingleUserLocked() { // Create the primary user - UserInfo primary = new UserInfo(0, + UserInfo primary = new UserInfo(0, mContext.getResources().getString(com.android.internal.R.string.owner_name), null, UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED); mUsers.put(0, primary); @@ -749,7 +757,7 @@ public class UserManagerService extends IUserManager.Stub { if (userHandle == 0 || user == null) { return false; } - mRemovingUserIds.add(userHandle); + mRemovingUserIds.put(userHandle, true); // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. @@ -813,13 +821,25 @@ public class UserManagerService extends IUserManager.Stub { } } - private void removeUserStateLocked(int userHandle) { + private void removeUserStateLocked(final int userHandle) { // Cleanup package manager settings mPm.cleanUpUserLILPw(userHandle); // Remove this user from the list mUsers.remove(userHandle); - mRemovingUserIds.remove(userHandle); + + // Have user ID linger for several seconds to let external storage VFS + // cache entries expire. This must be greater than the 'entry_valid' + // timeout used by the FUSE daemon. + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + synchronized (mPackagesLock) { + mRemovingUserIds.delete(userHandle); + } + } + }, MINUTE_IN_MILLIS); + // Remove user file AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); userFile.delete(); @@ -906,14 +926,13 @@ public class UserManagerService extends IUserManager.Stub { */ private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { - int i = mNextUserId; + int i = MIN_USER_ID; while (i < Integer.MAX_VALUE) { - if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { + if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) { break; } i++; } - mNextUserId = i + 1; return i; } } @@ -938,7 +957,7 @@ public class UserManagerService extends IUserManager.Stub { UserInfo user = mUsers.valueAt(i); if (user == null) continue; pw.print(" "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber); - if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> "); + if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> "); if (user.partial) pw.print(" <partial>"); pw.println(); pw.print(" Created: "); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b871cdc..0e29882 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -508,6 +508,10 @@ public class WifiManager { private Messenger mWifiServiceMessenger; private final CountDownLatch mConnected = new CountDownLatch(1); + private static Object sThreadRefLock = new Object(); + private static int sThreadRefCount; + private static HandlerThread sHandlerThread; + /** * Create a new WifiManager instance. * Applications will almost always want to use @@ -1365,9 +1369,14 @@ public class WifiManager { return; } - HandlerThread t = new HandlerThread("WifiManager"); - t.start(); - mHandler = new ServiceHandler(t.getLooper()); + synchronized (sThreadRefLock) { + if (++sThreadRefCount == 1) { + sHandlerThread = new HandlerThread("WifiManager"); + sHandlerThread.start(); + } + } + + mHandler = new ServiceHandler(sHandlerThread.getLooper()); mAsyncChannel.connect(mContext, mHandler, mWifiServiceMessenger); try { mConnected.await(); @@ -1983,8 +1992,10 @@ public class WifiManager { protected void finalize() throws Throwable { try { - if (mHandler != null && mHandler.getLooper() != null) { - mHandler.getLooper().quit(); + synchronized (sThreadRefLock) { + if (--sThreadRefCount == 0 && sHandlerThread != null) { + sHandlerThread.getLooper().quit(); + } } } finally { super.finalize(); diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 4c5fc5d..5e25623 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -61,7 +61,7 @@ public class WifiNative { /* Sends a kill signal to supplicant. To be used when we have lost connection or when the supplicant is hung */ - public native static boolean killSupplicant(); + public native static boolean killSupplicant(boolean p2pSupported); private native boolean connectToSupplicant(String iface); diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 040ff24..dafa8e8 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -1944,6 +1944,7 @@ public class WifiStateMachine extends StateMachine { case CMD_STOP_DRIVER: case CMD_DELAYED_STOP_DRIVER: case CMD_DRIVER_START_TIMED_OUT: + case CMD_CAPTIVE_CHECK_COMPLETE: case CMD_START_AP: case CMD_START_AP_SUCCESS: case CMD_START_AP_FAILURE: @@ -2189,6 +2190,13 @@ public class WifiStateMachine extends StateMachine { loge("Unable to change interface settings: " + ie); } + /* Stop a running supplicant after a runtime restart + * Avoids issues with drivers that do not handle interface down + * on a running supplicant properly. + */ + if (DBG) log("Kill any running supplicant"); + mWifiNative.killSupplicant(mP2pSupported); + if(mWifiNative.startSupplicant(mP2pSupported)) { if (DBG) log("Supplicant start successful"); mWifiMonitor.startMonitoring(); @@ -2384,7 +2392,7 @@ public class WifiStateMachine extends StateMachine { case WifiMonitor.SUP_DISCONNECTION_EVENT: if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) { loge("Failed to setup control channel, restart supplicant"); - mWifiNative.killSupplicant(); + mWifiNative.killSupplicant(mP2pSupported); transitionTo(mDriverLoadedState); sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); } else { @@ -2451,7 +2459,7 @@ public class WifiStateMachine extends StateMachine { break; case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ loge("Connection lost, restart supplicant"); - mWifiNative.killSupplicant(); + mWifiNative.killSupplicant(mP2pSupported); mWifiNative.closeSupplicantConnection(); mNetworkInfo.setIsAvailable(false); handleNetworkDisconnect(); @@ -2605,14 +2613,14 @@ public class WifiStateMachine extends StateMachine { /* Socket connection can be lost when we do a graceful shutdown * or when the driver is hung. Ensure supplicant is stopped here. */ - mWifiNative.killSupplicant(); + mWifiNative.killSupplicant(mP2pSupported); mWifiNative.closeSupplicantConnection(); transitionTo(mDriverLoadedState); break; case CMD_STOP_SUPPLICANT_FAILED: if (message.arg1 == mSupplicantStopFailureToken) { loge("Timed out on a supplicant stop, kill and proceed"); - mWifiNative.killSupplicant(); + mWifiNative.killSupplicant(mP2pSupported); mWifiNative.closeSupplicantConnection(); transitionTo(mDriverLoadedState); } |
