jix 进行从事Android系统源码开发不得不在原有的系统上内置自己的App。通过内置App一般都需要调用些系统才能访问的系统级App。App的部署和调试需要依赖源码系统。通过命令 : mm 来实现。
第三方App想调用内置的app需要通过跨进程调用。
这里通过AIDL来实现跨进程调用。
首先声明AIDL文件,
Android源码工程的文件构成和格式和标准的app完全不一样。
为了方便调试,先在标准的App中调试通过。
再copy标准工程到源码App工程里。
声明的AIDL文件:
Callback.aidl
package com.android.kvservice;interface Callback {oneway void onMessageReceived(int type, String value);
}
KvInterface.aidl
package com.android.kvservice;import com.android.kvservice.Callback;interface KvInterface {void registerCallback(Callback callback);void unregisterCallback(Callback callback);void sendMessage(int type, String value);
}
AIDL的文件夹放的位置
注意在build.gradle 里声明: aidl true
plugins {alias(libs.plugins.androidApplication)alias(libs.plugins.jetbrainsKotlinAndroid)
}android {namespace 'com.yyy.xxx.service'compileSdk 34defaultConfig {applicationId "com.yyy.xxx.kvservice"minSdk 29targetSdk 34versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"vectorDrawables {useSupportLibrary true}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}buildFeatures {compose trueviewBinding trueaidl true}composeOptions {kotlinCompilerExtensionVersion '1.5.1'}packaging {resources {excludes += '/META-INF/{AL2.0,LGPL2.1}'}}}dependencies {implementation libs.androidx.core.ktximplementation libs.androidx.lifecycle.runtime.ktximplementation libs.androidx.activity.composeimplementation platform(libs.androidx.compose.bom)implementation libs.androidx.uiimplementation libs.androidx.ui.graphicsimplementation libs.androidx.ui.tooling.previewimplementation libs.androidx.material3testImplementation libs.junitandroidTestImplementation libs.androidx.junitandroidTestImplementation libs.androidx.espresso.coreandroidTestImplementation platform(libs.androidx.compose.bom)androidTestImplementation libs.androidx.ui.test.junit4debugImplementation libs.androidx.ui.toolingdebugImplementation libs.androidx.ui.test.manifest
}
实现AIDL接口的地方
import android.content.Context;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.StatFs;
import android.os.storage.StorageVolume;
import android.util.Log;
import android.os.storage.DiskInfo;
import android.os.storage.VolumeInfo;
import android.os.storage.StorageManager;
import android.app.usage.StorageStatsManager;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.storage.StorageEntry;
import com.android.server.kvservice.storage.StorageUtils;import java.util.List;public class KVService extends KvInterface.Stub {/*** 获取系统全部内存大小,包括隐藏的内存*/
// public static final int GET_SYSTEM_STORGE_TOTAL = 1;public static final int GET_ALL_STORGE = 1;/****/public static final int GET_SYSTEM_STORGE_REMAIN = 2;public static final int GET_SDCARD_TOTAL = 3;public static final int GET_SDCARD_REMAIN = 4;private static final String TAG = KVService.class.getSimpleName();private RemoteCallbackList<Callback> mCallbackList = new RemoteCallbackList<>();private Context mContext;private Object lack = new Object();private StorageManager storageManager = null;public KVService(Context context) {this.mContext = context;storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);Log.d(TAG, "henryservice init");}@Overridepublic void registerCallback(Callback callback) {boolean result = mCallbackList.register(callback);Log.d(TAG, "register pid:" + Binder.getCallingPid()+ " uid:" + Binder.getCallingUid() + " result:" + result);}@Overridepublic void unregisterCallback(Callback callback) {boolean result = mCallbackList.unregister(callback);Log.d(TAG, "unregister pid:" + Binder.getCallingPid()+ " uid:" + Binder.getCallingUid() + " result:" + result);}@Overridepublic void sendMessage(int type, String value) throws RemoteException {String result = new String("no-data");if (type == GET_ALL_STORGE) {
// ....}Log.e(TAG, "rev the type : =========== " + type);Log.e(TAG, "rev the value : =========== " + value);sendEventToRemote(type, result);}public void sendEventToRemote(int type, String value) {synchronized (lack) {int count = mCallbackList.getRegisteredCallbackCount();Log.d(TAG, "remote callback count:" + count);if (count > 0) {// 注意: 遍历过程如果存在多线程操作, 需要加锁, 不然可能会抛出异常final int size = mCallbackList.beginBroadcast();for (int i = 0; i < size; i++) {Callback cb = mCallbackList.getBroadcastItem(i);try {if (cb != null) {cb.onMessageReceived(type, value);}} catch (RemoteException e) {e.printStackTrace();Log.d(TAG, "remote exception:" + e.getMessage());}}mCallbackList.finishBroadcast();}}}}
实现Service的地方:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;import androidx.annotation.Nullable;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.KVService;public class KService extends Service {private KVService kvService = null;@Overridepublic void onCreate() {super.onCreate();if (kvService == null) {kvService = new KVService(this);}}@Nullable@Overridepublic IBinder onBind(Intent intent) {return binder;}private final KvInterface.Stub binder = new KvInterface.Stub() {@Overridepublic void registerCallback(Callback callback) throws RemoteException {Log.e("KService", " registerCallback ============ ");kvService.registerCallback(callback);Log.e("KService", " registerCallback ============ end");}@Overridepublic void unregisterCallback(Callback callback) throws RemoteException {Log.e("KService", " unregisterCallback ============ ");kvService.unregisterCallback(callback);Log.e("KService", " unregisterCallback ============ end");}@Overridepublic void sendMessage(int type, String value) throws RemoteException {kvService.sendMessage(type, value);Log.e("KService", " sendMessage ============ end");}};
}
本地调用Service的代码。
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;import androidx.annotation.Nullable;import com.android.kvservice.Callback;
import com.android.kvservice.KvInterface;
import com.android.server.kvservice.KVService;public class MainActivity extends Activity {// private MainBinding binding;private KvInterface kvInterface;private Button btnStart;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
// binding = MainBinding.inflate(getLayoutInflater());
// setContentView(binding.getRoot());setContentView(R.layout.main);btnStart = findViewById(R.id.btnStart);btnStart.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (kvInterface == null) {bindToService();}}});Button btnSend = findViewById(R.id.btnSend);btnSend.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.e("TAG", "send 111");registerCallback();if (kvInterface != null) {try {Log.e("TAG", "send 222");kvInterface.sendMessage(1, "");} catch (RemoteException e) {e.printStackTrace();}}}});}private boolean register = false;private Callback.Stub callback = new Callback.Stub() {@Overridepublic void onMessageReceived(int type, String value) throws RemoteException {Log.e("MainActivity", "rev the type:: ==== " + type);Log.e("MainActivity", "rev the value:: ==== " + value);}};private void registerCallback() {if (!register) {register = true;try {if (kvInterface != null) {kvInterface.registerCallback(callback);}} catch (Exception e) {e.printStackTrace();}}}@Overrideprotected void onDestroy() {super.onDestroy();try {if (kvInterface != null) {kvInterface.unregisterCallback(callback);}} catch (RemoteException e) {throw new RuntimeException(e);}}private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {kvInterface = KvInterface.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {kvInterface = null;}};private void bindToService() {
// bindService(new Intent("com.kingview.qti.service.KService"), serviceConnection, Service.BIND_AUTO_CREATE);bindService(new Intent(this, KService.class), serviceConnection, Service.BIND_AUTO_CREATE);Log.e("TAG","bindToService ======================");}
}
为了第三方App调用,需要再配置文件中做以下声明:
<serviceandroid:name="com.kingview.qti.service.KService"android:enabled="true"android:exported="true"android:process="com.kingview.qti.service.kservice"><intent-filter><action android:name="com.kingview.service.kservice" /></intent-filter></service>
第三方App调用AIDL服务的代码,发现调用Action并没有什么用,特别注意要输入完整的包名:
private fun bindToService() {val intent = Intent()intent.setComponent(ComponentName("com.kingview.qti.service","com.kingview.qti.service.KService"))bindService(intent,serviceConnection,BIND_AUTO_CREATE)
// Log.e("StorageTestActivity", " bindToService ====================== ")}
注册远程回调
private fun registerCallback() {if (!register) {register = truetry {if (kvInterface != null) {kvInterface!!.registerCallback(callback)}kvInterface?.sendMessage(1, "")} catch (e: Exception) {e.printStackTrace()}}}
反注册callback和 解绑Service。
override fun onDestroy() {super.onDestroy()kvInterface?.unregisterCallback(callback)unbindService(serviceConnection)}
监听实现的操作是这样实现的:
@Volatileprivate var register = falseprivate val callback: Callback = object : Callback.Stub() {@Throws(RemoteException::class)override fun onMessageReceived(type: Int, value: String) {Log.e("StorageTestActivity", "rev the type:: ==== $type")Log.e("StorageTestActivity", "rev the value:: ==== $value")//解析数据,并显示到界面上if (testKey == KVApplication.TEST_STORAGE) {val strs = value.split("#").filter { v ->v.trim() != ""}for (v in strs) {val bean = Gson().fromJson<StorageBean>(v, StorageBean::class.java)if (bean.description.contains("internal_storage")) {val total = bean.total.toLong() / 1000f / 1000f / 1000f;val used = bean.used.toLong() / 1000f / 1000f / 1000f;val remain = total - usedbinding.txtStatus.post {binding.txtSize.text = " Size: ${total} GB ; Remain ${remain} GB. \n "}}}}}}
通过以上操作你可能会发现,依然无法调用远程的Service。你需要再AndroidManifest里声明一个权限。
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"tools:ignore="QueryAllPackagesPermission" />
将App放置在源码工程的的 package/apps/里创建一个文件夹如:XYZService
src/ res/ Androidmanifast.xml 都要从标准App工程里copy出来。
Android.bp这样写,包含了AIDL文件 :
//
// Copyright (C) 2013 Google Inc.
//package {default_applicable_licenses: ["packages_apps_kvservice_license"],
}// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {name: "packages_apps_kvservice_license",visibility: [":__subpackages__"],license_kinds: ["SPDX-license-identifier-BSD",],// large-scale-change unable to identify any license_text files
}android_app {name: "XYZServiceTest",defaults: ["platform_app_defaults"],platform_apis: true,certificate: "platform",system_ext_specific: true,privileged: true,srcs: ["src/**/*.java","src/**/*.aidl",],aidl: {local_include_dirs: ["src/aidl"],},manifest: "AndroidManifest.xml",resource_dirs: ["res",],// privileged: true,// sdk_version: "33",// sdk_version: "current",// certificate: "platform",// platform_apis: true,// product_specific: true,static_libs: ["androidx.core_core",// "androidx.annotation:annotation:1.3.0","guava",],
}
通过阅读Setting源码移植的代码如下:
public static List<StorageEntry> getAllStorageEntries(Context context,StorageManager storageManager) {final List<StorageEntry> storageEntries = new ArrayList<>();storageEntries.addAll(storageManager.getVolumes().stream().filter(volumeInfo -> isStorageSettingsInterestedVolume(volumeInfo)).map(volumeInfo -> new StorageEntry(context, volumeInfo)).collect(Collectors.toList()));storageEntries.addAll(storageManager.getDisks().stream().filter(disk -> isDiskUnsupported(disk)).map(disk -> new StorageEntry(disk)).collect(Collectors.toList()));storageEntries.addAll(storageManager.getVolumeRecords().stream().filter(volumeRecord -> isVolumeRecordMissed(storageManager, volumeRecord)).map(volumeRecord -> new StorageEntry(volumeRecord)).collect(Collectors.toList()));return storageEntries;}
public class StorageEntry implements Comparable<StorageEntry>, Parcelable {private final VolumeInfo mVolumeInfo;private final DiskInfo mUnsupportedDiskInfo;private final VolumeRecord mMissingVolumeRecord;private final String mVolumeInfoDescription;public StorageEntry(@NonNull Context context, @NonNull VolumeInfo volumeInfo) {mVolumeInfo = volumeInfo;mUnsupportedDiskInfo = null;mMissingVolumeRecord = null;if (isDefaultInternalStorage()) {// Shows "This device" for default internal storage.mVolumeInfoDescription = "storage_default_internal_storage";} else {mVolumeInfoDescription = context.getSystemService(StorageManager.class).getBestVolumeDescription(mVolumeInfo);}}public StorageEntry(@NonNull DiskInfo diskInfo) {mVolumeInfo = null;mUnsupportedDiskInfo = diskInfo;mMissingVolumeRecord = null;mVolumeInfoDescription = null;}public StorageEntry(@NonNull VolumeRecord volumeRecord) {mVolumeInfo = null;mUnsupportedDiskInfo = null;mMissingVolumeRecord = volumeRecord;mVolumeInfoDescription = null;}private StorageEntry(Parcel in) {mVolumeInfo = in.readParcelable(VolumeInfo.class.getClassLoader());mUnsupportedDiskInfo = in.readParcelable(DiskInfo.class.getClassLoader());mMissingVolumeRecord = in.readParcelable(VolumeRecord.class.getClassLoader());mVolumeInfoDescription = in.readString();}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeParcelable(mVolumeInfo, 0 /* parcelableFlags */);out.writeParcelable(mUnsupportedDiskInfo, 0 /* parcelableFlags */);out.writeParcelable(mMissingVolumeRecord, 0 /* parcelableFlags */);out.writeString(mVolumeInfoDescription);}public static final Parcelable.Creator<StorageEntry> CREATOR =new Parcelable.Creator<StorageEntry>() {public StorageEntry createFromParcel(Parcel in) {return new StorageEntry(in);}public StorageEntry[] newArray(int size) {return new StorageEntry[size];}};@Overridepublic boolean equals(Object o) {if (o == this) {return true;}if (!(o instanceof StorageEntry)) {return false;}final StorageEntry StorageEntry = (StorageEntry) o;if (isVolumeInfo()) {return mVolumeInfo.equals(StorageEntry.mVolumeInfo);}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);}return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);}@Overridepublic int hashCode() {if (isVolumeInfo()) {return mVolumeInfo.hashCode();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.hashCode();}return mMissingVolumeRecord.hashCode();}@Overridepublic String toString() {if (isVolumeInfo()) {return mVolumeInfo.toString();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.toString();}return mMissingVolumeRecord.toString();}@Overridepublic int compareTo(StorageEntry other) {if (isDefaultInternalStorage() && !other.isDefaultInternalStorage()) {return -1;}if (!isDefaultInternalStorage() && other.isDefaultInternalStorage()) {return 1;}if (isVolumeInfo() && !other.isVolumeInfo()) {return -1;}if (!isVolumeInfo() && other.isVolumeInfo()) {return 1;}if (isPrivate() && !other.isPrivate()) {return -1;}if (!isPrivate() && other.isPrivate()) {return 1;}if (isMounted() && !other.isMounted()) {return -1;}if (!isMounted() && other.isMounted()) {return 1;}if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {return -1;}if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {return 1;}if (getDescription() == null) {return 1;}if (other.getDescription() == null) {return -1;}return getDescription().compareTo(other.getDescription());}/*** Returns default internal storage.*/public static StorageEntry getDefaultInternalStorageEntry(Context context) {return new StorageEntry(context, context.getSystemService(StorageManager.class).findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));}/*** If it's a VolumeInfo.*/public boolean isVolumeInfo() {return mVolumeInfo != null;}/*** If it's an unsupported DiskInfo.*/public boolean isDiskInfoUnsupported() {return mUnsupportedDiskInfo != null;}/*** If it's a missing VolumeRecord.*/public boolean isVolumeRecordMissed() {return mMissingVolumeRecord != null;}/*** If it's a default internal storage.*/public boolean isDefaultInternalStorage() {if (isVolumeInfo()) {return mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE&& TextUtils.equals(mVolumeInfo.getId(), VolumeInfo.ID_PRIVATE_INTERNAL);}return false;}/*** If it's a mounted storage.*/public boolean isMounted() {return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED|| mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);}/*** If it's an unmounted storage.*/public boolean isUnmounted() {return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);}/*** If it's an unmountable storage.*/public boolean isUnmountable() {return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;}/*** If it's a private storage.*/public boolean isPrivate() {return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;}/*** If it's a public storage.*/public boolean isPublic() {return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;}/*** Returns description.*/public String getDescription() {if (isVolumeInfo()) {return mVolumeInfoDescription;}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getDescription();}return mMissingVolumeRecord.getNickname();}/*** Returns ID.*/public String getId() {if (isVolumeInfo()) {return mVolumeInfo.getId();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getId();}return mMissingVolumeRecord.getFsUuid();}/*** Returns disk ID.*/public String getDiskId() {if (isVolumeInfo()) {return mVolumeInfo.getDiskId();}if (isDiskInfoUnsupported()) {return mUnsupportedDiskInfo.getId();}return null;}/*** Returns fsUuid.*/public String getFsUuid() {if (isVolumeInfo()) {return mVolumeInfo.getFsUuid();}if (isDiskInfoUnsupported()) {return null;}return mMissingVolumeRecord.getFsUuid();}/*** Returns root file if it's a VolumeInfo.*/public File getPath() {return mVolumeInfo == null ? null : mVolumeInfo.getPath();}/*** Returns VolumeInfo of the StorageEntry.*/public VolumeInfo getVolumeInfo() {return mVolumeInfo;}
}
Setting关于计算存储卡的代码在
StorageUsageProgressBarPreferenceController
StorageDashboardFragment文件