AndroidX 做為一個后臺 Service 應(yīng)用,開機自動運行,配合系統(tǒng)做一些定制化功能,并且對外提供 API。
主要功能:
● 硬件看門狗代理
● USB Host/Device 切換
● 4G 網(wǎng)絡(luò)保活
● 系統(tǒng)日志寫入文件
● 鍵值攔截
● 啟用應(yīng)用
項目地址:https://github.com/aystshen/AndroidX
硬件看門狗代理
硬件看門狗代理主要負責(zé)下面幾項工作:
● 對外提供看門狗 API,比如:打開關(guān)閉看門狗、設(shè)置超時時長、獲取超時時長、判斷看門狗是否打開。
● 定時發(fā)送看門狗心跳(喂狗)。
● 恢復(fù)看門狗狀態(tài)(因進入 OTA 升級和用戶關(guān)機時,會臨時關(guān)閉看門狗,當(dāng)升級完成或重新開機需要恢復(fù)看門狗狀態(tài))。
API
// IWatchdogService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IWatchdogService {
boolean openWatchdog();
boolean closeWatchdog();
boolean setWatchdogTimeout(int timeout);
int getWatchdogTimeout();
boolean watchdogIsOpen();
}
使用
1、在 APP 源碼 aidl/android/os/ 目錄下新建 IWatchdogService.aidl,如下:
// IWatchdogService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IWatchdogService {
boolean openWatchdog();
boolean closeWatchdog();
boolean setWatchdogTimeout(int timeout);
int getWatchdogTimeout();
boolean watchdogIsOpen();
}
2.實現(xiàn)下面代碼:
Intent intent = new Intent();
intent.setPackage("com.ayst.androidx");
intent.setAction("com.ayst.androidx.WATCHDOG_SERVICE");
mContext.bindService(intent, mWatchdogServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mWatchdogServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "IWatchdogService, onServiceConnected...");
mWatchdogService = IWatchdogService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "IWatchdogService, onServiceDisconnected...");
mWatchdogService = null;
}
};
/**
* 打開、關(guān)閉看門狗
*
* @param on
*/
public void toggleWatchdog(boolean on) {
if (null != mWatchdogService) {
try {
if (on) {
mWatchdogService.openWatchdog();
} else {
mWatchdogService.closeWatchdog();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 設(shè)置看門狗超時時長
*
* @param timeout
*/
public void setWatchdogTimeout(int timeout) {
if (null != mWatchdogService) {
try {
mWatchdogService.setWatchdogTimeout(timeout);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
USB Host/Device 切換
通常為了復(fù)用 USB 功能,使同一個 USB 口可以同時做為 Host 和 Device(調(diào)試) 口使用,我們可以通過軟件來切換功能。
Android 默認提供了切換方法,不同的系統(tǒng)文件路徑可能不同,如下:
# 路徑1,向下面文件寫入:0:自動,1:HOST,2:OTG
/sys/bus/platform/drivers/usb20_otg/force_usb_mode
# 路徑2, 向下面文件寫入:peripheral:自動,host:HOST,otg:OTG
/sys/devices/platform/ff770000.syscon/ff770000.syscon:usb2-phy@e450/otg_mode
這里為了提供更統(tǒng)一的方法,我們封裝 Java API。
API
// IOtgService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IOtgService {
boolean setOtgMode(String mode);
String getOtgMode();
}
1、使用
在 APP 源碼 aidl/android/os/ 目錄下新建 IOtgService.aidl,如下:
// IOtgService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IOtgService {
boolean setOtgMode(String mode);
String getOtgMode();
}
2.實現(xiàn)下面代碼:
Intent intent = new Intent();
intent.setPackage("com.ayst.androidx");
intent.setAction("com.ayst.androidx.OTG_SERVICE");
mContext.bindService(intent, mOtgServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mOtgServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "IOtgService, onServiceConnected...");
mOtgService = IOtgService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "IOtgService, onServiceDisconnected...");
mOtgService = null;
}
};
/**
* 調(diào)otg口usb工作模式
*
* @param mode
* 0:auto,由硬件決定
* 1:host,usb模式
* 2:device,otg調(diào)試模式
*/
public void setOtgMode(String mode) {
if (null != mOtgService) {
try {
if (!mOtgService.setOtgMode(mode)) {
mAndroidXView.updateAndroidXOtgMode(mOtgService.getOtgMode());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 獲取當(dāng)前otg模式
*
* @return
* 0:auto,由硬件決定
* 1:host,usb模式
* 2:device,otg調(diào)試模式
*/
public String getOtgMode() {
if (null != mOtgService) {
try {
return mOtgService.getOtgMode();
} catch (RemoteException e) {
e.printStackTrace();
}
}
return "0";
}
4G 網(wǎng)絡(luò)?;?/strong>
考慮到一些 4G 模塊長時間運行的穩(wěn)定性,可能會出現(xiàn) 4G 模塊工作異常而導(dǎo)致網(wǎng)絡(luò)斷開并無法恢復(fù)。為了保證 4G 網(wǎng)絡(luò)在長時間掉網(wǎng)后能夠自行恢復(fù),增加 4G 網(wǎng)絡(luò)?;罘?wù),當(dāng)網(wǎng)絡(luò)長時間掉網(wǎng)并不能自恢復(fù)時,直接復(fù)位 4G 模塊,使 4G 模塊重啟工作。
API
// IModemService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IModemService {
boolean open4gKeepLive();
boolean close4gKeepLive();
boolean keepLiveIsOpen();
}
1.在 APP 源碼 aidl/android/os/ 目錄下新建 IModemService.aidl,如下:
// IModemService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IModemService {
boolean open4gKeepLive();
boolean close4gKeepLive();
boolean keepLiveIsOpen();
}
2.實現(xiàn)下面代碼:.
Intent intent = new Intent();
intent.setPackage("com.ayst.androidx");
intent.setAction("com.ayst.androidx.MODEM_SERVICE");
mContext.bindService(intent, mModemServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mModemServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "IModemService, onServiceConnected...");
mModemService = IModemService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "IModemService, onServiceDisconnected...");
mModemService = null;
}
};
/**
* 打開、關(guān)閉4G?;罘?wù)
*
* @param on
*/
public void toggle4GKeepLive(boolean on) {
if (null != mModemService) {
try {
if (on) {
mModemService.open4gKeepLive();
} else {
mModemService.close4gKeepLive();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
系統(tǒng)日志寫入文件
為了方便產(chǎn)品上線后 debug,我們默認將 logcat 日志和內(nèi)核日志寫入文件保存,debug 時可以隨時導(dǎo)出分析問題。日志文件采用循環(huán)保存,默認最多保存10個日志文件,支持設(shè)置,單個日志文件最大20M。
日志路徑:/sdcard/lastlog/
cnnho:/sdcard/lastlog/android $ ls -l
total 12880
-rw-rw---- 1 root sdcard_rw 1288359 2020-03-02 17:26 2020-03-02_17-23-09.log
-rw-rw---- 1 root sdcard_rw 1301092 2020-03-02 17:31 2020-03-02_17-26-44.log
-rw-rw---- 1 root sdcard_rw 1466984 2020-03-02 17:40 2020-03-02_17-32-23.log
-rw-rw---- 1 root sdcard_rw 1212094 2020-03-02 17:43 2020-03-02_17-41-11.log
-rw-rw---- 1 root sdcard_rw 1328673 2020-03-02 17:49 2020-03-02_17-44-05.log
-rw-rw---- 1 root sdcard_rw 2403373 2020-03-02 18:38 2020-03-02_17-50-00.log
-rw-rw---- 1 root sdcard_rw 79347 2020-03-02 18:41 2020-03-02_18-38-16.log
-rw-rw---- 1 root sdcard_rw 1292712 2020-03-02 18:53 2020-03-02_18-49-41.log
-rw-rw---- 1 root sdcard_rw 1567416 2020-03-02 19:06 2020-03-02_18-53-59.log
-rw-rw---- 1 root sdcard_rw 1227984 2020-03-02 19:09 2020-03-02_19-07-29.log
API
// ILog2fileService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface ILog2fileService {
void openLog2file();
void closeLog2file();
boolean isOpen();
boolean setLogFileNum(int num);
int getLogFileNum();
}
1.在 APP 源碼 aidl/android/os/ 目錄下新建 ILog2fileService.aidl,如下:
// ILog2fileService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface ILog2fileService {
void openLog2file();
void closeLog2file();
boolean isOpen();
boolean setLogFileNum(int num);
int getLogFileNum();
}
2.實現(xiàn)下面代碼:
Intent intent = new Intent();
intent.setPackage("com.ayst.androidx");
intent.setAction("com.ayst.androidx.LOG2FILE_SERVICE");
mContext.bindService(intent, mLog2fileServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mLog2fileServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "ILog2fileService, onServiceConnected...");
mLog2fileService = ILog2fileService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "ILog2fileService, onServiceDisconnected...");
mLog2fileService = null;
}
};
/**
* 打開、關(guān)閉日志寫入文件
*
* @param on true:打開 false:關(guān)閉
*/
public void toggleLog2file(boolean on) {
if (null != mLog2fileService) {
try {
if (on) {
mLog2fileService.openLog2file();
} else {
mLog2fileService.closeLog2file();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
/**
* 設(shè)置最大保存日志文件數(shù)
*
* @param num 最大日志文件數(shù)
* @return true:成功 false:失敗
*/
private boolean setLogFileNum(int num) {
if (null != mLog2fileService) {
try {
return mLog2fileService.setLogFileNum(num);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 獲取最大保存日志文件數(shù)
*
* @return 最大日志文件數(shù)
*/
private int getLogFileNum() {
if (null != mLog2fileService) {
try {
return mLog2fileService.getLogFileNum();
} catch (RemoteException e) {
e.printStackTrace();
}
}
return 0;
}
鍵值攔截
一些支持遙控器操作的產(chǎn)品上,默認只允許管理員使用遙控器操作設(shè)備,普通用戶只能操作『上下左右』鍵,當(dāng)操作其它鍵時需要先按『NUM_LOCK』鍵,然后輸入預(yù)設(shè)的密碼(默認密碼:『上上下下左右左右』)才可以解鎖后使用。
API
// IKeyInterceptService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IKeyInterceptService {
void openKeyIntercept();
void closeKeyIntercept();
boolean isOpen();
}
使用
1.在 APP 源碼 aidl/android/os/ 目錄下新建 IKeyInterceptService.aidl,如下:
// IKeyInterceptService.aidl
package com.ayst.androidx;
// Declare any non-default types here with import statements
interface IKeyInterceptService {
void openKeyIntercept();
void closeKeyIntercept();
boolean isOpen();
}
2.實現(xiàn)下面代碼:
Intent intent = new Intent();
intent.setPackage("com.ayst.androidx");
intent.setAction("com.ayst.androidx.KEY_INTERCEPT_SERVICE");
mContext.bindService(intent, mKeyInterceptServiceConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mKeyInterceptServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "IKeyInterceptService, onServiceConnected...");
mIKeyInterceptService = IKeyInterceptService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "IKeyInterceptService, onServiceDisconnected...");
mIKeyInterceptService = null;
}
};
/**
* 打開、關(guān)閉鍵值攔截
*
* @param on
*/
public void toggleKeyIntercept(boolean on) {
if (null != mIKeyInterceptService) {
try {
if (on) {
mIKeyInterceptService.openKeyIntercept();
} else {
mIKeyInterceptService.closeKeyIntercept();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
啟用應(yīng)用
一些產(chǎn)品為了方便作業(yè)人員安裝測試設(shè)備,需要臨時停用某些應(yīng)用,有時會忘記恢復(fù),從而導(dǎo)致后面客戶應(yīng)用無法運行的問題。
該方案在通過屬性『ro.enableapps』配置開機后需要自動啟用的應(yīng)用包名,開機后運行Service重新啟用之前停用的應(yīng)用。
關(guān)鍵實現(xiàn)
使用 PackageManager 提供的方法:文章來源:http://www.zghlxwxcb.cn/news/detail-489803.html
/**
* Set the enabled setting for an application
* This setting will override any enabled state which may have been set by the application in
* its manifest. It also overrides the enabled state set in the manifest for any of the
* application's components. It does not override any enabled state set by
* {@link #setComponentEnabledSetting} for any of the application's components.
*
* @param packageName The package name of the application to enable
* @param newState The new enabled state for the application.
* @param flags Optional behavior flags.
*/
public abstract void setApplicationEnabledSetting(String packageName,
@EnabledState int newState, @EnabledFlags int flags);
public class AppEnableService extends Service {
private static final String TAG = "AppEnableService";
private String[] mEnableApps;
private Thread mEnableThread;
public AppEnableService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand...");
mEnableApps = loadApps();
if (null != mEnableApps && mEnableApps.length > 0) {
mEnableThread = new Thread(mEnableRunnable);
mEnableThread.start();
} else {
Log.w(TAG, "onStartCommand, There is no application to be enabled.");
}
return Service.START_REDELIVER_INTENT;
}
private String[] loadApps() {
String value = AppUtils.getProperty("ro.enableapps", "");
if (!TextUtils.isEmpty(value)) {
return value.split(",");
}
return null;
}
private Runnable mEnableRunnable = new Runnable() {
@Override
public void run() {
PackageManager pm = getPackageManager();
for (String pkgName : mEnableApps) {
if (!TextUtils.isEmpty(pkgName)
&& pkgName.contains(".")) {
pm.setApplicationEnabledSetting(
pkgName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
0);
Log.i(TAG, pkgName + " Enabled.");
}
}
}
};
}
屬性配置
通過屬性『ro.enableapps』配置應(yīng)用包名,支持同時配置多個包名,用逗號隔開。例如:文章來源地址http://www.zghlxwxcb.cn/news/detail-489803.html
ro.enableapps=com.ayst.sample1,com.ayst.sample2
到了這里,關(guān)于【干貨】Android系統(tǒng)定制基礎(chǔ)篇:第六部分-Android擴展服務(wù)-AndroidX的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!