Jeff Brown a2910d0abb Make it easier to create asynchronous Handlers.
There are potentially very many Handlers owned by services
that should not be blocked by barriers introduced by UI traversals
occurring on the same thread (if that ever happens).
Add some convenience constructors to make it easy to switch
these Handlers over to being async.

Bug: 7057752
Change-Id: I64d9bffe81e7c52ada4cfad4e89d4340153f4688
2012-08-25 13:40:26 -07:00

255 lines
9.8 KiB
Java

/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server;
import static android.provider.Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK;
import static android.provider.Settings.Secure.SCREENSAVER_ENABLED;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
import android.util.Log;
import android.util.Slog;
import java.io.FileNotFoundException;
import java.io.FileReader;
/**
* <p>DockObserver monitors for a docking station.
*/
final class DockObserver extends UEventObserver {
private static final String TAG = DockObserver.class.getSimpleName();
private static final boolean LOG = false;
private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
private static final int DEFAULT_SCREENSAVER_ENABLED = 1;
private static final int DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK = 1;
private static final int MSG_DOCK_STATE_CHANGED = 0;
private final Object mLock = new Object();
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
private boolean mSystemReady;
private final Context mContext;
public DockObserver(Context context) {
mContext = context;
init(); // set initial status
startObserving(DOCK_UEVENT_MATCH);
}
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Dock UEVENT: " + event.toString());
}
synchronized (mLock) {
try {
int newState = Integer.parseInt(event.get("SWITCH_STATE"));
if (newState != mDockState) {
mPreviousDockState = mDockState;
mDockState = newState;
if (mSystemReady) {
// Don't force screen on when undocking from the desk dock.
// The change in power state will do this anyway.
// FIXME - we should be configurable.
if ((mPreviousDockState != Intent.EXTRA_DOCK_STATE_DESK
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_LE_DESK
&& mPreviousDockState != Intent.EXTRA_DOCK_STATE_HE_DESK) ||
mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
PowerManager pm =
(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
pm.wakeUp(SystemClock.uptimeMillis());
}
updateLocked();
}
}
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
}
private void init() {
synchronized (mLock) {
try {
char[] buffer = new char[1024];
FileReader file = new FileReader(DOCK_STATE_PATH);
try {
int len = file.read(buffer, 0, 1024);
mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
mPreviousDockState = mDockState;
} finally {
file.close();
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "This kernel does not have dock station support");
} catch (Exception e) {
Slog.e(TAG, "" , e);
}
}
}
void systemReady() {
synchronized (mLock) {
// don't bother broadcasting undocked here
if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
updateLocked();
}
mSystemReady = true;
}
}
private void updateLocked() {
mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
}
private void handleDockStateChange() {
synchronized (mLock) {
Slog.i(TAG, "Dock state changed: " + mDockState);
final ContentResolver cr = mContext.getContentResolver();
if (Settings.Secure.getInt(cr,
Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
Slog.i(TAG, "Device not provisioned, skipping dock broadcast");
return;
}
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
// Check if this is Bluetooth Dock
// TODO(BT): Get Dock address.
// String address = null;
// if (address != null) {
// intent.putExtra(BluetoothDevice.EXTRA_DEVICE,
// BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address));
// }
// User feedback to confirm dock connection. Particularly
// useful for flaky contact pins...
if (Settings.System.getInt(cr,
Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) {
String whichSound = null;
if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
if ((mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
(mPreviousDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
(mPreviousDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_UNDOCK_SOUND;
} else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_UNDOCK_SOUND;
}
} else {
if ((mDockState == Intent.EXTRA_DOCK_STATE_DESK) ||
(mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
(mDockState == Intent.EXTRA_DOCK_STATE_HE_DESK)) {
whichSound = Settings.System.DESK_DOCK_SOUND;
} else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) {
whichSound = Settings.System.CAR_DOCK_SOUND;
}
}
if (whichSound != null) {
final String soundPath = Settings.System.getString(cr, whichSound);
if (soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
if (sfx != null) {
sfx.setStreamType(AudioManager.STREAM_SYSTEM);
sfx.play();
}
}
}
}
}
IDreamManager mgr = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams"));
if (mgr != null) {
// dreams feature enabled
boolean undocked = mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED;
if (undocked) {
try {
if (mgr.isDreaming()) {
mgr.awaken();
}
} catch (RemoteException e) {
Slog.w(TAG, "Unable to awaken!", e);
}
} else {
if (isScreenSaverEnabled(mContext) && isScreenSaverActivatedOnDock(mContext)) {
try {
mgr.dream();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to dream!", e);
}
}
}
} else {
// dreams feature not enabled, send legacy intent
mContext.sendStickyBroadcast(intent);
}
}
}
private static boolean isScreenSaverEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
SCREENSAVER_ENABLED, DEFAULT_SCREENSAVER_ENABLED) != 0;
}
private static boolean isScreenSaverActivatedOnDock(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
SCREENSAVER_ACTIVATE_ON_DOCK, DEFAULT_SCREENSAVER_ACTIVATED_ON_DOCK) != 0;
}
private final Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DOCK_STATE_CHANGED:
handleDockStateChange();
break;
}
}
};
}