功能:ACC状态通过内核switch上报,系统通过Observer监测状态变化,根据状态变化做灭屏和关机的操作。
1.添加frameworks/base/services/core/java/com/android/server/AccObserver.java
package com.android.server;
import android.content.ContentResolver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.content.Intent;
import android.content.IntentFilter;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
public class AccObserver extends SystemService {
private static final String TAG = "AccObserver";
private static final String ACC_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/car-acc";
private static final String ACC_STATE_PATH = "sys/class/switch/car-acc/state";
private static final int MSG_ACC_STATE_CHANGED = 1;
private static final int MSG_ACC_SCREEN_ON_ACTION = 2;
private static final int MSG_ACC_SCREEN_OFF_ACTION = 3;
private static final int MSG_ACC_SHUTDOWN_ACTION = 4;
private final PowerManager mPowerManager;
private final PowerManager.WakeLock mWakeLock;
private final Object mLock = new Object();
private boolean mSystemReady;
private int mActualAccState = Intent.EXTRA_ACC_STATE_OFF;
private int mReportedAccState = -1;
private int mPreviousAccState = Intent.EXTRA_ACC_STATE_OFF;
private boolean mUpdatesStopped;
private long mAcquireLockTime;
private Context mContext;
private int screen_state_flag =0;
private Intent mAccIntent;
/**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
* and passes it to super.
* </p>
*
* @param context The system server context.
*/
public AccObserver(Context context) {
super(context);
mContext = context;
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = mPowerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG);
mAccIntent = new Intent("com.waysion.acc.screen_to_off");//通知Settings
mObserver.startObserving(ACC_UEVENT_MATCH);
}
@Override
public void onStart() {
publishBinderService(TAG, new BinderService());
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
synchronized (mLock) {
mSystemReady = true;
// don't bother broadcasting undocked here
if (mReportedAccState != Intent.EXTRA_ACC_STATE_OFF) {
//updateLocked();
}
BootReceiver mBootReceiver = new BootReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(mBootReceiver, filter);
AccControlReceiver mAccControlReceiver = new AccControlReceiver();
IntentFilter accfilter = new IntentFilter();
accfilter.addAction("acc_setting_screen_control");
accfilter.addAction("acc_setting_shutdown_control");
mContext.registerReceiver(mAccControlReceiver, accfilter);
}
}
}
private void init() {
synchronized (mLock) {
try {
char[] buffer = new char[1024];
FileReader file = new FileReader(ACC_STATE_PATH);
try {
int len = file.read(buffer, 0, 1024);
mActualAccState = Integer.valueOf((new String(buffer, 0, len)).trim());
mPreviousAccState = mActualAccState;
} finally {
file.close();
}
} catch (FileNotFoundException e) {
Log.w(TAG, "This kernel does not have acc station support");
} catch (Exception e) {
Log.e(TAG, "" , e);
}
}
}
private void setActualAccStateLocked(int newState) {
mActualAccState = newState;
if (!mUpdatesStopped) {
setAccStateLocked(newState);
}
}
private void setAccStateLocked(int newState) {
if (newState != mReportedAccState) {
mReportedAccState = newState;
if (mSystemReady) {
mHandler.sendEmptyMessage(MSG_ACC_STATE_CHANGED);
}
}
}
private void updateLocked() {
mAcquireLockTime = SystemClock.uptimeMillis();
mWakeLock.acquire(5000);
mHandler.sendEmptyMessage(MSG_ACC_STATE_CHANGED);
}
private void handleDockStateChange() {
synchronized (mLock) {
// 读取数据库判断是否开启acc功能
boolean enabling = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ACC_PROVISIONED, 0) != 0);
if (!enabling) {
Log.i(TAG, "ACC disable !! return !!");
return;
}
//TODO
Log.i(TAG, "ACC state changed from " + mPreviousAccState + " to "
+ mReportedAccState);
if (mReportedAccState == -1) {
return;
}
final int previousDockState = mPreviousAccState;
mPreviousAccState = mReportedAccState;
// Skip the acc intent if not yet provisioned.
final ContentResolver cr = getContext().getContentResolver();
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_ACC_EVENT);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(Intent.EXTRA_ACC_STATE, mReportedAccState);
// Send the acc event intent.
// There are many components in the system watching for this so as to
// adjust audio routing, screen orientation, etc.
getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
String screenValue = SystemProperties.get("persist.sys.acc_screen","0");
String shutdownValue = SystemProperties.get("persist.sys.acc_shutdown","0");
String screenDelay = SystemProperties.get("persist.sys.screen_delay","10");
String shutdownDelay = SystemProperties.get("persist.sys.shutdown_delay","10");
//TODO 执行ACC开关需要的操作
if (mReportedAccState == 1) {
if(screenValue.equals("1")){
mHandler.removeMessages(MSG_ACC_SCREEN_OFF_ACTION);
mHandler.sendEmptyMessage(MSG_ACC_SCREEN_ON_ACTION);
}else{
mHandler.removeMessages(MSG_ACC_SCREEN_OFF_ACTION);
mHandler.removeMessages(MSG_ACC_SHUTDOWN_ACTION);
}
}else {
if(screenValue.equals("1")){
mHandler.sendEmptyMessageDelayed(MSG_ACC_SCREEN_OFF_ACTION,Integer.parseInt(screenDelay)*1000);
}
if(shutdownValue.equals("1")){
mHandler.sendEmptyMessageDelayed(MSG_ACC_SHUTDOWN_ACTION,Integer.parseInt(shutdownDelay)*1000);
}
}
}
}
private final Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ACC_STATE_CHANGED:
handleDockStateChange();
//TODO
if (mWakeLock.isHeld() && (SystemClock.uptimeMillis() - mAcquireLockTime < 4000)) {
mWakeLock.release();
}
break;
case MSG_ACC_SCREEN_ON_ACTION:
screenOn();
break;
case MSG_ACC_SCREEN_OFF_ACTION:
screenOff();
break;
case MSG_ACC_SHUTDOWN_ACTION:
if(screen_state_flag==1){
screenOn();
}
Intent intent_shutdown = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent_shutdown.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent_shutdown.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent_shutdown, UserHandle.CURRENT);
break;
}
}
};
private final class AccControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "action is " + intent.getAction());
if (intent.getAction().equals("acc_setting_screen_control")){
mHandler.removeMessages(MSG_ACC_SCREEN_OFF_ACTION);
}else if(intent.getAction().equals("acc_setting_shutdown_control")){
mHandler.removeMessages(MSG_ACC_SHUTDOWN_ACTION);
}
}
}
//observer event
private final UEventObserver mObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
try {
synchronized (mLock) {
String gpio_state = getState(event.toString(), "SWITCH_STATE");
Log.i(TAG,"gpio_state="+gpio_state);
setActualAccStateLocked("1".equals(gpio_state)?1:0);
}
} catch (NumberFormatException e) {
Log.e(TAG, "Could not parse switch state from event " + event);
}
}
};
private String getState(String strs, String name) {
char[] strArr = strs.toCharArray();
String result = "";
if(strs.contains(name)) {
for(int index=strs.indexOf(name) + name.length(); strArr[index] !=','; index++) {
if(strArr[index] != '=') {
result += strArr[index];
}
}
} else {
Log.e(TAG, "no contains " + name);
}
return result.replace(" ", "");
}
private final class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "action is " + intent.getAction());
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
synchronized(mLock) {
Log.i(TAG, "mActualAccState is " + mActualAccState);
init();
if (mActualAccState == Intent.EXTRA_ACC_STATE_OFF) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
setActualAccStateLocked(Intent.EXTRA_ACC_STATE_OFF);//放送handler
} catch (InterruptedException e) {
Log.w(TAG, "exception happend");
}
}
}).start();
}
}
}
}
}
private final class BinderService extends Binder {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump acc observer service from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
pw.println("Current Acc Observer Service state:");
if (mUpdatesStopped) {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
pw.println(" reported state: " + mReportedAccState);
pw.println(" previous state: " + mPreviousAccState);
pw.println(" actual state: " + mActualAccState);
} else if (args.length == 3 && "set".equals(args[0])) {
String key = args[1];
String value = args[2];
try {
if ("state".equals(key)) {
mUpdatesStopped = true;
setAccStateLocked(Integer.parseInt(value));
} else {
pw.println("Unknown set option: " + key);
}
} catch (NumberFormatException ex) {
pw.println("Bad value: " + value);
}
} else if (args.length == 1 && "reset".equals(args[0])) {
mUpdatesStopped = false;
setAccStateLocked(mActualAccState);
} else {
pw.println("Dump current acc state, or:");
pw.println(" set state <value>");
pw.println(" reset");
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/**
* 亮屏
*/
public void screenOn() {
mPowerManager.wakeUp(SystemClock.uptimeMillis());
mWakeLock.acquire();
mAccIntent.putExtra("screen_off_state","0");
mContext.sendBroadcastAsUser(mAccIntent, UserHandle.ALL);
screen_state_flag=0;
Log.w(TAG, "screenOn");
}
/**
* 释放并直接灭屏
*/
public void screenOff() {
mAccIntent.putExtra("screen_off_state","1");
mContext.sendBroadcastAsUser(mAccIntent, UserHandle.ALL);
mWakeLock.acquire();
mWakeLock.release();
screen_state_flag=1;
mPowerManager.goToSleep(SystemClock.uptimeMillis());
Log.w(TAG, "screenOff");
}
}
2.frameworks/base/core/java/android/provider/Settings.java 增加
/**
* Whether the acc has been provisioned (0 = false, 1 = true)
*/
public static final String ACC_PROVISIONED = "acc_provisioned";
3.修改frameworks/base/core/java/android/content/Intent.java
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d08a471..ce246ab 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2733,6 +2733,10 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_DOCK_EVENT =
"android.intent.action.DOCK_EVENT";
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACC_EVENT =
+ "android.intent.action.ACC_EVENT";
+
/**
* Broadcast Action: A broadcast when idle maintenance can be started.
* This means that the user is not interacting with the device and is
@@ -3969,6 +3973,10 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final int EXTRA_DOCK_STATE_HE_DESK = 4;
+ public static final String EXTRA_ACC_STATE = "android.intent.extra.ACC_STATE";
+ public static final int EXTRA_ACC_STATE_ON = 1;
+ public static final int EXTRA_ACC_STATE_OFF = 0;
4.更新api current.txt
diff --git a/api/current.txt b/api/current.txt
index 2cb6f49..3f01723 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8531,6 +8531,7 @@ package android.content {
method public deprecated java.lang.String toURI();
method public java.lang.String toUri(int);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ACTION_ACC_EVENT = "android.intent.action.ACC_EVENT";
field public static final java.lang.String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
field public static final java.lang.String ACTION_ALL_APPS = "android.intent.action.ALL_APPS";
field public static final java.lang.String ACTION_ANSWER = "android.intent.action.ANSWER";
@@ -8689,6 +8690,9 @@ package android.content {
field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final java.lang.String CATEGORY_VOICE = "android.intent.category.VOICE";
field public static final android.os.Parcelable.Creator<android.content.Intent> CREATOR;
+ field public static final java.lang.String EXTRA_ACC_STATE = "android.intent.extra.ACC_STATE";
+ field public static final int EXTRA_ACC_STATE_OFF = 0; // 0x0
+ field public static final int EXTRA_ACC_STATE_ON = 1; // 0x1
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
@@ -32472,6 +32476,7 @@ package android.provider {
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
+ field public static final java.lang.String ACC_PROVISIONED = "acc_provisioned";
diff --git a/api/system-current.txt b/api/system-current.txt
index 788c087..392e5c4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8871,6 +8871,7 @@ package android.content {
method public deprecated java.lang.String toURI();
method public java.lang.String toUri(int);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ACTION_ACC_EVENT = "android.intent.action.ACC_EVENT";
field public static final java.lang.String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
field public static final java.lang.String ACTION_ALL_APPS = "android.intent.action.ALL_APPS";
field public static final java.lang.String ACTION_ANSWER = "android.intent.action.ANSWER";
@@ -9036,6 +9037,9 @@ package android.content {
field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final java.lang.String CATEGORY_VOICE = "android.intent.category.VOICE";
field public static final android.os.Parcelable.Creator<android.content.Intent> CREATOR;
+ field public static final java.lang.String EXTRA_ACC_STATE = "android.intent.extra.ACC_STATE";
+ field public static final int EXTRA_ACC_STATE_OFF = 0; // 0x0
+ field public static final int EXTRA_ACC_STATE_ON = 1; // 0x1
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
@@ -35228,6 +35232,7 @@ package android.provider {
method public static boolean putInt(android.content.ContentResolver, java.lang.String, int);
method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
+ field public static final java.lang.String ACC_PROVISIONED = "acc_provisioned";
5.frameworks/base/services/java/com/android/server/SystemServer.java 中启动服务
startOtherServices 函数中增加
boolean disableAcc = SystemProperties.getBoolean("config.disable_acc", false);
if (!disableAcc) {
Slog.d(TAG, "start service AccObserver.class");
mSystemServiceManager.startService(AccObserver.class);
}