Android自定义系统服务端监控客户端存活状态并管理客户端占有的资源

140 阅读2分钟

问题背景

在定制系统中,往往需要在自定义系统服务中实现硬件资源的控制接口,app调用了这些接口后,还没来得及释放就因为种种原因崩溃了。未释放的硬件资源可能会导致系统休眠功耗过高,或其它app用了该未释放硬件资源导致错误等等。

如CustomSystemService中实现了个接口power_ctrl()用于控制打印机,扫描等外设的供电。app在调用了power_ctrl()将电源开启后,在后面的运行过程中有时会因为其它原因导致app崩溃,但电源还开着未关闭,影响了机器的休眠功耗。因此需要有个方案来监控CustomSystemService客户端app的存活状态,并在app异常崩溃后将对应的资源释放掉,包括但不限于外设电源这种资源。

怎么添加自定义服务和对外提供接口可以参考:在Android中利用抽象类对外提供系统接口

实现

原理

在app所持有的CustomManagerImpl对象中创建一个IBinder对象,并将其传给服务端作为客户端app的代表,这时其实对CustomSystemService来说,CustomManagerImpl也是一个Binder服务端,可以通过IBinder.DeathRecipient判断CustomManagerImpl持有者是否存活。

步骤:

在CustomManagerImpl.java中新增一个成员变量IBinder mToken

private final IBinder mToken;

app在实例化CustomManager时会调用CustomManagerImpl(Context context, ICustomSystemService service):

public CustomManagerImpl(Context context, ICustomSystemService service) {
    mService = service;
    mContext = context;
    mToken = new Binder();
    mHandler = new MyHandler(mContext.getMainLooper());
}

在CustomManagerImpl.java中调用ptr_power_on(IBinder costomClient)时,传入mToken

public void ptr_power_on() {
    try {
        mService.ptr_power_on(mToken);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

CustomSystemService中:
ptr_power_on的实现:

public void ptr_power_on(IBinder customClient) {
    power_ctrl(costomClient, true);
}

private synchronized void power_ctrl(IBinder client, boolean on) {
    CustomClient customClient = findOrCreatCustomClient(client);
    if (on) {
        customClient.mPwrEnCnt ++;
        setGpioValue(GPIO_CTRL_NUM_PWREN, 1);
    } else {
        if (customClient.mPwrEnCnt > 0) {
            customClient.mPwrEnCnt--;
        }
        boolean NotNeedOff =  false;
        /* 所有 client 都执行了 power off 才真正执行下电动作 */
        for (CustomClient c : mCustomClients) {
            if (c.mPwrEnCnt > 0) {
                NotNeedOff = true;
                break;
            }
        }
        if (!NotNeedOff) {
            setGpioValue(GPIO_CTRL_NUM_PWREN, 0);
        }
    }
    Log.d(TAG, "power_ctrl: " + customClient.mTag + " mPwrEnCnt = " + customClient.mPwrEnCnt);
}

其中CustomClient的定义如下:

private final class CustomClient implements IBinder.DeathRecipient {
    public final IBinder mClient;
    public String mTag;
    public final String mPackageName;
    public int mPwrEnCnt = 0;

    public CustomClient(IBinder client, String tag, String packageName) {
        mClient = client;
        mTag = tag;
        mPackageName = packageName;
        Log.d(TAG, "CostomClient: " + tag + ", " + packageName);
    }

    @Override
    public void binderDied() {
        CostomSystemService.this.handleWakeLockDeath(this);
    }
}

mCostomClients:

private final ArrayList<CostomClient> mCostomClients = new ArrayList<CostomClient>();

findOrCreatCostomClient():

private CostomClient findOrCreatCostomClient(IBinder client) {
    synchronized (mLock) {
        int index = findCostomClientIndexLocked(client);
        if (index >= 0) {
            return mCostomClients.get(index);
        } else {
            String callingApp = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
            CostomClient costomClient = new CostomClient(client, Binder.getCallingPid() + ":" + client, callingApp);
            try {
                client.linkToDeath(costomClient, 0);
            } catch (RemoteException ex) {
                throw new IllegalArgumentException("costomClient is already dead.");
            }
            mCostomClients.add(costomClient);
            return costomClient;
        }
    }
}

在findOrCreatCostomClient()中,client.linkToDeath(costomClient, 0)注册了客户端死掉后,服务端需调用的方法也就是CostomClient.binderDied()。

CostomClient.binderDied()接着调用CostomSystemService.handleWakeLockDeath()

private void handleWakeLockDeath(CostomClient costomClient) {
    synchronized (mLock) {
        Log.d(TAG, "handleWakeLockDeath: client =" + Objects.hashCode(costomClient.mClient)
                + " [" + costomClient.mTag + "]");

        int index = mCostomClients.indexOf(costomClient);
        if (index < 0) {
            return;
        }

        removeCostomClientLocked(costomClient, index);
    }
}

removeCostomClientLocked():

private void removeCostomClientLocked(CostomClient costomClient, int index) {
    if (costomClient.mPwrEnCnt > 0) {
        costomClient.mPwrEnCnt = 0;
        power_ctrl(costomClient.mClient, false);
    }
    mCostomClients.remove(index);
}

在removeCostomClientLocked()中对死掉客户端所占用的资源进行了清理。