本篇文章主要內(nèi)容如下:
- 1草巡、在PackageManagerService的installPackageAsUser方法里面的代碼
- 2嗤瞎、DefaultContainerService詳解
- 3漓拾、mContainerService.getMinimalPackageInfo(String.int,String)方法與calculateInstalledSize(String,boolean,String)方法的講解
- 4壳影、為什么說mContext.bindServiceAsUser等于mContext.bindService
- 5、HandlerParams與InstallParams簡(jiǎn)介
- 6肥荔、InstallArgs家族成員
- 7绿渣、為什么新安裝的情況下 origin.staged等于false
- 8、LocalSocket的跨進(jìn)程通信
- 9燕耿、createInstallArgs(InstallParams)方法解答
- 10中符、sVerificationEnabled(int userId, int installFlags) 的理解
- 11、Context.sendBroadcast(Intent intent)的功能是和Context.sendBroadcastAsUser(Intent,UserHandle)一樣的解答
- 12誉帅、Split APK(APK拆分)與Instant Run簡(jiǎn)介
一淀散、在PackageManagerService的installPackageAsUser方法里面的代碼mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null)
這個(gè)方法調(diào)用在PackageManagerService.java 9524行
(一) mContext是什么?
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null)
要看這個(gè)方法內(nèi)部執(zhí)行蚜锨,首先要知道這個(gè)mContext是什么档插,我們知道這個(gè)mContext是通過PackageManagerService的main方法傳入的,所以這個(gè)mContext就是SystemServer里面的mSystemContext亚再。
代碼在SystemServer.java 366行如下:
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
這樣我來追蹤下mSystemContext
我們找到了mSystemContext的初始化的地方在createSystemContext()里面
代碼在SystemServer.java 311行如下:
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
那我們就來追中下ActivityThread 的getSystemContext()方法
代碼在ActivityThread.java 1886行如下:
public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
mSystemContext = ContextImpl.createSystemContext(this);
}
return mSystemContext;
}
}
進(jìn)而追蹤到ContextImpl里面代碼在ContextImpl.java 1774行郭膛。
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
ContextImpl context = new ContextImpl(null, mainThread,
packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
context.mResourcesManager.getDisplayMetricsLocked());
return context;
}
所以我們知道這個(gè)SystemContext其實(shí)就是ContextImpl,這就好辦了氛悬,我們直接找ContextImpl對(duì)應(yīng)的enforceCallingOrSelfPermission方法
代碼在ContextImpl.java 1468行
@Override
public void enforceCallingOrSelfPermission(
String permission, String message) {
enforce(permission,
checkCallingOrSelfPermission(permission),
true,
Binder.getCallingUid(),
message);
}
這個(gè)方法首先調(diào)用了checkCallingOrSelfPermission方法饲鄙,然后再調(diào)用enforce方法,那我們就依次看下圆雁。
(二)ContextImpl#checkCallingOrSelfPermission(String) 方法 簡(jiǎn)介
代碼在ContextImpl.java 1416行
@Override
public int checkCallingOrSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission, Binder.getCallingPid(),
Binder.getCallingUid());
}
這個(gè)方法首先做了入?yún)ermission的非空判斷忍级,然后調(diào)用了checkPermission(String,int,int )方法
而在ContextImpl里面checkPermission(String,int,int )方法如下,代碼在ContextImpl.java 1374行:
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
try {
return ActivityManagerNative.getDefault().checkPermission(
permission, pid, uid);
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
}
我們看到的是最后調(diào)用的是 ActivityManagerNative.getDefault().checkPermission(permission, pid, uid);這里先提前說下這個(gè)方法其實(shí)是調(diào)用的ActivityServcieManager的checkPermission(String,int ,int)方法伪朽,關(guān)于為什么會(huì)這樣轴咱,我們后面講解Activity的啟動(dòng)流程的時(shí)候會(huì)詳細(xì)講解。
在ActivityManagerService.java里面checkPermission(String,int,int )方法如下,代碼在ActivityManagerService.java 7108行:
/**
* As the only public entry point for permissions checking, this method
* can enforce the semantic that requesting a check on a null global
* permission is automatically denied. (Internally a null permission
* string is used when calling {@link #checkComponentPermission} in cases
* when only uid-based security is needed.)
*
* This can be called with or without the global lock held.
*/
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
哈哈朴肺,有注釋了窖剑,我最喜歡有注釋的方法了,先簡(jiǎn)單翻譯一下注釋的內(nèi)容:
權(quán)限檢查的唯一公共入口戈稿。這個(gè)方法可以強(qiáng)制執(zhí)行對(duì)全局權(quán)限請(qǐng)求的檢查和如果是空的權(quán)限可以自動(dòng)拒絕西土。如果是在uid安全的情況,如果想使用空的字符串來檢查權(quán)限可以調(diào)用checkComponentPermission這個(gè)方法鞍盗。
這個(gè)方法可以在有或者沒有全局鎖定的情況下使用需了。
通過上面注釋,我們知道了這是一個(gè)全局的檢查權(quán)限的入口了般甲。我們看方法內(nèi)部最后調(diào)用了checkComponentPermission方法了肋乍,那我們就繼續(xù)跟蹤
int checkComponentPermission(String , int , int , int , boolean )方法如下,代碼在ActivityManagerService.java 7089行:
/**
* This can be called with or without the global lock held.
*/
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
通過注釋我們知道敷存,這個(gè)方法可以在有全局鎖定或者沒有全局鎖定的時(shí)候調(diào)用墓造。這個(gè)方法內(nèi)部只做了一個(gè)MY_PID的判斷,如果pid=MY_PID锚烦,而MY_PID其實(shí)就是當(dāng)前進(jìn)程的pid觅闽,則直接返回PackageManager.PERMISSION_GRANTED,而PackageManager.PERMISSION_GRANTED表示的意思是"授予"涮俄,即檢查通過谱煤。那我們來看下ActivityManager.checkComponentPermission(permission, uid, owningUid, exported);這個(gè)方法的具體執(zhí)行
代碼在ActivityManager.java) 2617行
/** @hide */
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
// 首先判斷是不是root和system,如果是直接返回"授予"禽拔,因?yàn)樗鼈儞碛凶畲髾?quán)限
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions. 一般用不到刘离,需要在AndroidManifest里面設(shè)置android:isolatedProcess=true
// 判斷是否是隔離進(jìn)程 如果是隔離進(jìn)程則直接拒絕
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
// 如果是同一個(gè)應(yīng)用,則不需要監(jiān)測(cè)
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
// 如果設(shè)置了exported=false睹栖,(比如在AndroidManifest里面設(shè)置了exported=false) 則表明這個(gè)APP沒有授權(quán)硫惕,所以拒絕
if (!exported) {
/*
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
here);
*/
return PackageManager.PERMISSION_DENIED;
}
// 如果permission==null 則通過
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
// 否則調(diào)用AppGlobals.getPackageManager().checkUidPermission(permission,uid)
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
// Should never happen, but if it does... deny!
Slog.e(TAG, "PackageManager is dead?!?", e);
}
return PackageManager.PERMISSION_DENIED;
}
在講解這個(gè)方法的時(shí)候先補(bǔ)充一個(gè)知識(shí)點(diǎn),即在Android的系統(tǒng)中野来,每一個(gè)APP都會(huì)分配一個(gè)uid恼除,但是一個(gè)APP內(nèi)部可能會(huì)有多進(jìn)程,所以APP的內(nèi)部就可能存在不同的pid曼氛,但是其APP內(nèi)部的進(jìn)程共享一個(gè)uid豁辉。
方法內(nèi)部注釋已經(jīng)解釋的很清楚了,這里說下最后的AppGlobals.getPackageManager().checkUidPermission(permission,uid)方法舀患,如果成功調(diào)用則直接放回徽级,如果拋異常了,則返回拒絕(PackageManager.PERMISSION_DENIED)聊浅,那我們就來看下AppGlobals.getPackageManager().checkUidPermission(permission, uid);里面的具體實(shí)現(xiàn)
這里首先要看下AppGlobals.getPackageManager()的值是什么餐抢?
代碼在AppGlobals.java 46行现使。
/**
* Return the raw interface to the package manager.
* @return The package manager.
*/
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager();
}
我們前面的文章APK安裝流程詳解3——PackageManager與PackageManagerService我們知道最后到了PackageManagerService里面,咦好像又回來了旷痕。所以我們知道AppGlobals.getPackageManager()=PackageManagerService對(duì)象碳锈。所以AppGlobals.getPackageManager().checkUidPermission(permission, uid);這個(gè)方法其實(shí)可以理解為PackageManagerService#checkUidPermission(permission, uid)方法
代碼在PackageManagerService.java 3190行
@Override
public int checkUidPermission(String permName, int uid) {
final int userId = UserHandle.getUserId(uid);
// 判斷這個(gè)userId 對(duì)應(yīng)的App是否存在
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
synchronized (mPackages) {
// 獲取這個(gè)uid對(duì)應(yīng)的SettingBase
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
// PackageManagerService.Setting.mUserIds數(shù)組中,根據(jù)uid查找uid也就是package的權(quán)限列表
if (obj != null) {
final SettingBase ps = (SettingBase) obj;
// 獲取對(duì)應(yīng)的permissionsState
final PermissionsState permissionsState = ps.getPermissionsState();
// 如果permissionsState 里面包含這個(gè)permName欺抗,則通過
if (permissionsState.hasPermission(permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
// ACCESS_COARSE_LOCATION的特殊情況售碳,也是通過
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
// 如果上面都沒滿足,則拒絕權(quán)限
} else {
// 系統(tǒng)級(jí)應(yīng)用uid 對(duì)應(yīng)的permission
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
}
return PackageManager.PERMISSION_DENIED;
}
PS:我這里是Android 6.0的源碼绞呈,所以這個(gè)的代碼和Android 5.0是不同的贸人,所以有心的同學(xué)可以去對(duì)比下,Android6.0里面多了一個(gè)PermissionsState报强,Android 6.0以后是對(duì)權(quán)限的操作是PermissionsState灸姊。有興趣的同學(xué)可以研究下PermissionsState拱燃,和它的hasPermission(String name, int userId)方法秉溉,這里面包含了除了聲明的權(quán)限,還必須是授權(quán)的權(quán)限碗誉。
上面代碼注釋已經(jīng)寫的很清楚了召嘶,大家可以自行查看,自此)ContextImpl#checkCallingOrSelfPermission(String)整個(gè)方法流程就已經(jīng)跟蹤完畢哮缺。
(三)弄跌、ContextImpl#enforce(String,int,boolean,int,String)方法簡(jiǎn)介
代碼在ContextImpl.java 1434行
private void enforce(
String permission, int resultOfCheck,
boolean selfToo, int uid, String message) {
if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
(message != null ? (message + ": ") : "") +
(selfToo
? "Neither user " + uid + " nor current process has "
: "uid " + uid + " does not have ") +
permission +
".");
}
}
這里面很簡(jiǎn)單,主要判斷是上面checkCallingOrSelfPermission方法的返回值尝苇,如果不是PackageManager.PERMISSION_GRANTED則直接拋異常铛只,如果是,則什么也不做糠溜。
(四)淳玩、總結(jié)
通過上述方法的解析,我們知道ContextImple#enforceCallingOrSelfPermission經(jīng)過一些列的調(diào)用非竿,最后還是判斷這個(gè)APP的權(quán)限蜕着。
二、DefaultContainerService詳解
DefaultContainerService.java源碼地址
(一)红柱、DefaultContainerService類簡(jiǎn)介
/**
* Service that offers to inspect and copy files that may reside on removable
* storage. This is designed to prevent the system process from holding onto
* open files that cause the kernel to kill it when the underlying device is
* removed.
*/
public class DefaultContainerService extends IntentService {
...
}
首先我們知道DefaultContainerService繼承自IntentService承匣,然后為了更好的理解設(shè)計(jì)者的意圖,我們還是看下面的注釋
提供檢查和復(fù)制文件的Service锤悄,這個(gè)Service既可以提供保存在存儲(chǔ)空間的服務(wù)也可以是提供刪除服務(wù)韧骗。這樣設(shè)計(jì)的的目的是防止:在系統(tǒng)進(jìn)程打開文件的時(shí)候,同時(shí)如果底層設(shè)備刪除了文件而內(nèi)核將其殺死的情況的發(fā)生零聚。
所以我們總結(jié)一下DefaultContainerService是一個(gè)應(yīng)用服務(wù)宽闲,具體負(fù)責(zé)實(shí)現(xiàn)APK等相關(guān)資源文件在內(nèi)部或者外部存儲(chǔ)器上的存儲(chǔ)工作众眨。
(二)、DefaultContainerService類結(jié)構(gòu)
通過上面類結(jié)構(gòu)的圖容诬,我們發(fā)現(xiàn)這個(gè)類的方法大多數(shù)是私有的或者"包"內(nèi)的方法娩梨,所以只要找到源頭,基本上能捋順這個(gè)類览徒。而想要捋順這個(gè)類很簡(jiǎn)單狈定,因?yàn)樗^承IntentService,所以一般的Android開發(fā)工程師都是知道只要找他到的onHandlerIntent方法即可习蓬。那下面我們就來研究下這個(gè)方法
(三)纽什、onHandlerIntent(Intent)方法
代碼在DefaultContainerService.java 271行
@Override
protected void onHandleIntent(Intent intent) {
if (PackageManager.ACTION_CLEAN_EXTERNAL_STORAGE.equals(intent.getAction())) {
// 第一步
final IPackageManager pm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
PackageCleanItem item = null;
try {
// 第二步
while ((item = pm.nextPackageToClean(item)) != null) {
final UserEnvironment userEnv = new UserEnvironment(item.userId);
eraseFiles(userEnv.buildExternalStorageAppDataDirs(item.packageName));
eraseFiles(userEnv.buildExternalStorageAppMediaDirs(item.packageName));
if (item.andCode) {
eraseFiles(userEnv.buildExternalStorageAppObbDirs(item.packageName));
}
}
} catch (RemoteException e) {
}
}
}
我們把這個(gè)方法里面的內(nèi)容分為三大部分
- 第一步:獲取IPackageManager對(duì)象,其實(shí)也就是PackageManagerService的代理對(duì)象pm
- 第二步:遍歷pm即PackageManagerService中的已經(jīng)卸載了躲叼,但是仍然占用存儲(chǔ)空間的對(duì)象PackageCleanItem
- 第三步:調(diào)用eraseFiles()方法來清除文件
這里面涉及三個(gè)內(nèi)容
- 1芦缰、pm. nextPackageToClean(PackageCleanItem)方法
- 2、UserEnvironment類及其方法
- 3枫慷、本地的eraseFiles方法
那我們就依次來看下
1让蕾、pm. nextPackageToClean(PackageCleanItem)方法
我們知道pm其實(shí)是PackageManagerService的代理類,所以我們直接找PackageManagerService的nextPackageToClean(PackageCleanItem)方法
代碼在PackageManagerService.java 9457行
@Override
public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) {
// writer
synchronized (mPackages) {
// 第一步
if (!isExternalMediaAvailable()) {
// If the external storage is no longer mounted at this point,
// the caller may not have been able to delete all of this
// packages files and can not delete any more. Bail.
return null;
}
// 第二步
final ArrayList<PackageCleanItem> pkgs = mSettings.mPackagesToBeCleaned;
// 第三步
if (lastPackage != null) {
pkgs.remove(lastPackage);
}
// 第四步
if (pkgs.size() > 0) {
return pkgs.get(0);
}
}
return null;
}
這個(gè)方法內(nèi)部主要分為4步:
- 第一步:判斷外部設(shè)備是否可用
- 第二步:獲取已經(jīng)刪除了或听,但仍然占用存儲(chǔ)空間的列表
- 第三步:這一步其實(shí)是遞歸的一個(gè)思路探孝,如果是第一次調(diào)用nextPackageToClean,則lastPackage為null誉裆。如果不是第一次調(diào)用顿颅,則lastPackage為pkgs中目前元素的上一個(gè)元素。
- 第四步:獲取pkgs中的第0位的元素足丢,注意這里是get方法粱腻,這里獲取的元素,會(huì)在第三步中刪除的斩跌。
這里涉及到了mSettings.mPackagesToBeCleaned的概念绍些,那我們來看下這個(gè)變量是什么?
// Packages that have been uninstalled and still need their external
// storage data deleted.
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
通過注釋我們知道滔驶,這個(gè)mPackagesToBeCleaned變量表示的是:已經(jīng)卸載了遇革,但是仍占用外部存儲(chǔ)空間的軟件包。
至此pm. nextPackageToClean(PackageCleanItem)方法已經(jīng)跟蹤完畢
2揭糕、UserEnvironment類及其方法
UserEnvironment是Environment.java的靜態(tài)內(nèi)部類
UserEnvironment 我的理解就是某個(gè)應(yīng)用的存儲(chǔ)空間訪問類
我們常用的幾個(gè)方法是:
- buildExternalStorageAppCacheDirs(packageName):對(duì)應(yīng)sdcard/android/0/包名/cache 目錄
- buildExternalStorageAppDataDirs(packageName):對(duì)應(yīng)sdcard/android/0/包名/data 目錄
- buildExternalStorageAppMediaDirs(packageName):對(duì)應(yīng)sdcard/android/0/包名/media 目錄
- buildExternalStorageAppObbDirs(packageName):對(duì)應(yīng)sdcard/android/0/包名/obb 目錄
3萝快、eraseFiles(File[])方法
代碼在DefaultContainerService.java 290行
void eraseFiles(File[] paths) {
for (File path : paths) {
eraseFiles(path);
}
}
我們看到這個(gè)方法最后調(diào)用的重載的eraseFiles(String)方法
代碼在DefaultContainerService.java 296行
void eraseFiles(File path) {
if (path.isDirectory()) {
String[] files = path.list();
if (files != null) {
for (String file : files) {
eraseFiles(new File(path, file));
}
}
}
path.delete();
}
我們發(fā)現(xiàn)它使用遞歸的方法,依次刪除文件著角。
(四)揪漩、DefaultContainerService的重要變量mBinder
在DefaultContainerService里面有一個(gè)重要變量mBinder首昔。我們來看下
代碼在DefaultContainerService.java 72行
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
* Creates a new container and copies package there.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
* @param containerId the id of the secure container that should be used
* for creating a secure container into which the resource
* will be copied.
* @param key Refers to key used for encrypting the secure container
* @return Returns the new cache path where the resource has been copied
* into
*/
@Override
public String copyPackageToContainer(String packagePath, String containerId, String key,
boolean isExternal, boolean isForwardLocked, String abiOverride) {
if (packagePath == null || containerId == null) {
return null;
}
if (isExternal) {
// Make sure the sdcard is mounted.
String status = Environment.getExternalStorageState();
if (!status.equals(Environment.MEDIA_MOUNTED)) {
Slog.w(TAG, "Make sure sdcard is mounted.");
return null;
}
}
PackageLite pkg = null;
NativeLibraryHelper.Handle handle = null;
try {
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
handle = NativeLibraryHelper.Handle.create(pkg);
return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal,
isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath, e);
return null;
} finally {
IoUtils.closeQuietly(handle);
}
}
/**
* Copy package to the target location.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
* @return returns status code according to those in
* {@link PackageManager}
*/
@Override
public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
if (packagePath == null || target == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
PackageLite pkg = null;
try {
final File packageFile = new File(packagePath);
pkg = PackageParser.parsePackageLite(packageFile, 0);
return copyPackageInner(pkg, target);
} catch (PackageParserException | IOException | RemoteException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
/**
* Parse given package and return minimal details.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
*/
@Override
public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
String abiOverride) {
final Context context = DefaultContainerService.this;
final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
} else {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
}
return ret;
}
ret.packageName = pkg.packageName;
ret.splitNames = pkg.splitNames;
ret.versionCode = pkg.versionCode;
ret.baseRevisionCode = pkg.baseRevisionCode;
ret.splitRevisionCodes = pkg.splitRevisionCodes;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
pkg.packageName, pkg.installLocation, sizeBytes, flags);
ret.multiArch = pkg.multiArch;
return ret;
}
@Override
public ObbInfo getObbInfo(String filename) {
try {
return ObbScanner.getObbInfo(filename);
} catch (IOException e) {
Slog.d(TAG, "Couldn't get OBB info for " + filename);
return null;
}
}
@Override
public long calculateDirectorySize(String path) throws RemoteException {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final File dir = Environment.maybeTranslateEmulatedPathToInternal(new File(path));
if (dir.exists() && dir.isDirectory()) {
final String targetPath = dir.getAbsolutePath();
return MeasurementUtils.measureDirectory(targetPath);
} else {
return 0L;
}
}
@Override
public long[] getFileSystemStats(String path) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
try {
final StructStatVfs stat = Os.statvfs(path);
final long totalSize = stat.f_blocks * stat.f_bsize;
final long availSize = stat.f_bavail * stat.f_bsize;
return new long[] { totalSize, availSize };
} catch (ErrnoException e) {
throw new IllegalStateException(e);
}
}
@Override
public void clearDirectory(String path) throws RemoteException {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
final File directory = new File(path);
if (directory.exists() && directory.isDirectory()) {
eraseFiles(directory);
}
}
/**
* Calculate estimated footprint of given package post-installation.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
*/
@Override
public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
String abiOverride) throws RemoteException {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
}
}
};
通過上面代碼我們知道哮内,mBinder是IMediaContainerService.Stub類型蝠咆,看到這個(gè)類型啡邑,大家一定很熟了,對(duì)的一看就是AIDL昂勒。而且是AIDL的"服務(wù)端"蜀细。
看到AIDL我們首先要找他的源碼地址IMediaContainerService.aidl地址
PS:DefaultContainerService的onBind方法返回的就是mBinder
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
三、mContainerService.getMinimalPackageInfo(String.int,String)方法與calculateInstalledSize(String,boolean,String)方法的講解
(一)戈盈、mContainerService是什么奠衔?
先說下這個(gè)方法調(diào)用的位置:
在PackageManagerService中的handleStartCopy()方法里面
在代碼PackageManagerService.java 10597行
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
要想知道這個(gè)方法的具體流程,首要先要明確mContainerService是一個(gè)什么東西塘娶。
而mContainerService其實(shí)是IMediaContainerService類型的归斤,如下
private IMediaContainerService mContainerService = null;
我們?cè)賮碚蚁耺ContainerService初始化的位置:
在doHandleMessage(Message)方法里面MCS_BOUND的時(shí)候初始化的,在PackageManagerService.java 1173行
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
而這里面的msg.obj是在DefaultContainerConnection對(duì)象mDefContainerConn"綁定"連接DefaultContainerService的時(shí)候執(zhí)行onServiceConnected的時(shí)候初始化的刁岸。如下:
在PackageManagerService.java 928行
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
IMediaContainerService imcs =
IMediaContainerService.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
通過上面的跟蹤我們知道了mContainerService其實(shí)就是上面方法通過 IMediaContainerService.Stub.asInterface(service)來獲取的脏里,通過AIDL知識(shí)我們知道其實(shí)對(duì)應(yīng)的是DefaultContainerService的內(nèi)部變量mBinder。所以說
mContainerService對(duì)應(yīng)著DefaultContainerService的成員變量mBinder虹曙。所以mContainerService.getMinimalPackageInfo(String.int,String)方法對(duì)應(yīng)的是DefaultContainerService的成員變量mBinder的getMinimalPackageInfo(String.int,String)方法迫横。
(二)、mContainerService.getMinimalPackageInfo(String.int,String)方法
代碼在DefaultContainerService.java 152行
/**
* Parse given package and return minimal details.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
*/
@Override
public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
String abiOverride) {
// 第一步
final Context context = DefaultContainerService.this;
final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
PackageInfoLite ret = new PackageInfoLite();
// 第二步
if (packagePath == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
return ret;
}
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
} else {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
}
return ret;
}
// 第三步
ret.packageName = pkg.packageName;
ret.splitNames = pkg.splitNames;
ret.versionCode = pkg.versionCode;
ret.baseRevisionCode = pkg.baseRevisionCode;
ret.splitRevisionCodes = pkg.splitRevisionCodes;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
pkg.packageName, pkg.installLocation, sizeBytes, flags);
ret.multiArch = pkg.multiArch;
return ret;
}
先來看下注釋:
解析包并獲去小的安裝包內(nèi)容
- 入?yún)?packagePath:要復(fù)制包的絕對(duì)路徑根吁。這個(gè)目錄可以包含單個(gè)APK也可以包含多個(gè)APK
我將這個(gè)方法分為三個(gè)部分
- 第一步:初始化一些信息
- 第二步:解析packagePath對(duì)應(yīng)的安裝包员淫,獲取解析的出來的"輕"安裝包pkg
- 第三步:把解析出來的"輕"安裝包的屬性賦值給PackageInfoLite對(duì)象ret并返回
(三)合蔽、calculateInstalledSize(origin.resolvedPath, isForwardLocked(), packageAbiOverride);方法
代碼在DefaultContainerService.java 251行
/**
* Calculate estimated footprint of given package post-installation.
*
* @param packagePath absolute path to the package to be copied. Can be
* a single monolithic APK file or a cluster directory
* containing one or more APKs.
*/
@Override
public long calculateInstalledSize(String packagePath, boolean isForwardLocked,
String abiOverride) throws RemoteException {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return Long.MAX_VALUE;
}
}
先來看下注釋:
計(jì)算安裝包安裝后可能的大小
- 入?yún)?packagePath:這個(gè)目錄可以包含單個(gè)APK也可以包含多個(gè)APK
四击敌、為什么說mContext.bindServiceAsUser等于mContext.bindService
(一)先說下這個(gè)mContext.bindServiceAsUser在哪里被調(diào)用
在PackageManagerService.java1109
在connectToService()方法里面被調(diào)用
private boolean connectToService() {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
通過前文我們知道這里的mContext其實(shí)就是ContextImpl,所以我們看下這個(gè)bindServiceAsUser方法的具體實(shí)現(xiàn)
代碼在ContextImpl.java) 1291
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
/** @hide */
@Override
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);
}
大家看到?jīng)]拴事,bindService和bindServiceAsUser的內(nèi)部其實(shí)是用調(diào)用bindServiceCommon這個(gè)方法來實(shí)現(xiàn)的具體的邏輯的沃斤,所以說bindService方法和bindServiceAsUser其實(shí)內(nèi)部的執(zhí)行邏輯是一直的
五、HandlerParams與InstallParams簡(jiǎn)介
在PackageManagerService進(jìn)行安裝的時(shí)候會(huì)涉及兩個(gè)概念即HandlerParams與InstallParams刃宵,那我們就依次介紹下:
(一)衡瓶、HandlerParams類
代碼在PackageManagerService.java 10233行
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
/**
* Number of times startCopy() has been attempted and had a non-fatal
* error.
*/
private int mRetries = 0;
/** User handle for the user requesting the information or installation. */
private final UserHandle mUser;
HandlerParams(UserHandle user) {
mUser = user;
}
UserHandle getUser() {
return mUser;
}
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
final void serviceError() {
if (DEBUG_INSTALL) Slog.i(TAG, "serviceError");
handleServiceError();
handleReturnCode();
}
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
}
我們看到這類,就一個(gè)構(gòu)造函數(shù)牲证。而且在構(gòu)造這個(gè)類的時(shí)候哮针,需要傳入一個(gè)UserHandle
它里面有三個(gè)抽象方法
- abstract void handleStartCopy() throws RemoteException;
- abstract void handleServiceError();
- abstract void handleReturnCode();
有兩個(gè)核心非抽象方法,注意這兩個(gè)方法都是final的
- final startCopy():
- final serviceError():
startCopy()已經(jīng)在上一篇文章APK安裝流程詳解10——PMS中的新安裝流程中HandlerParams的startCopy方法講解了坦袍,而serviceError()里面其實(shí)是調(diào)用了兩個(gè)handleServiceError()和handleReturnCode()抽象方法
小結(jié)=·
其實(shí)這個(gè)HandlerParams類主要就做了2件事十厢,一個(gè)是抽象出三行為,即三個(gè)抽象方法捂齐,然后定義了重試4次蛮放,如果超過4次則放棄重試的規(guī)則。
下面我們就來看下InstallParams類
(二)奠宜、InstallParams類與HandlerParams的關(guān)系
代碼在PackageManagerService.java 10464行
class InstallParams extends HandlerParams {
...
}
首先我們知道InstallParams類繼承自HandlerParams包颁,且InstallParams不是抽象方法瞻想,所以InstallParams必然實(shí)現(xiàn)了HandlerParams所對(duì)應(yīng)的三個(gè)方法。
所以InstallParams與HandlerParams關(guān)系如下:
所以說HandlerParams有兩個(gè)子類娩嚼,分別是InstallParams和MeasureParams蘑险。
- InstallParams:用于處理APK的安裝
- MeasureParams:用于查詢某個(gè)已安裝的APK占據(jù)的存儲(chǔ)空間的大小,例如在設(shè)置程序中得到某個(gè)APK使用緩存文件的大小岳悟。
五漠其、InstallArgs家族成員
InstallArgs是PackageManagerService的靜態(tài)內(nèi)部類
代碼在PackageManagerService.java 10907行
static abstract class InstallArgs {
...
}
通過上面代碼我們知道InstallArgs是抽象類,我們看到InstallArgs是靜態(tài)類竿音,且不是"public"的和屎,所以InstallArgs的所有子類肯定都在PackageManagerService中
我們找到了三個(gè)子類如下:
- 1 FileInstallArgs:APK安裝在內(nèi)部存儲(chǔ)空間的時(shí)候使用的子類
- 2 AsecInstallArgs:安裝到sdcard或者ForwardLocked的時(shí)候使用的子類
- 3、MoveInstallArgs:移動(dòng)包的位置春瞬,比如從內(nèi)部存儲(chǔ)移動(dòng)到sdcard上的構(gòu)造方法中根據(jù)InstallParams會(huì)構(gòu)造出具體類型
設(shè)計(jì)這四個(gè)類的目的是什么意義柴信?
這樣設(shè)計(jì)的目的是:APK可以安裝在內(nèi)部存儲(chǔ)空間或者SD卡上,已經(jīng)安裝的APK也可以在內(nèi)部存儲(chǔ)和SD之間進(jìn)行移動(dòng)宽气,PackageManagerService為此設(shè)計(jì)了InstallArgs這個(gè)抽象類的數(shù)據(jù)結(jié)構(gòu)随常,它代表這三種情況通用的屬性,
這里我們用FileInstallArgs類舉例萄涯,說一下FileInstallArgs與InstallParams的關(guān)系
代碼如下:
/**
* Logic to handle installation of non-ASEC applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;
// Example topology:
// /data/app/com.example/base.apk
// /data/app/com.example/split_foo.apk
// /data/app/com.example/lib/arm/libfoo.so
// /data/app/com.example/lib/arm64/libfoo.so
// /data/app/com.example/dalvik/arm/base.apk@classes.dex
/** New install */
FileInstallArgs(InstallParams params) {
super(params.origin, params.move, params.observer, params.installFlags,
params.installerPackageName, params.volumeUuid, params.getManifestDigest(),
params.getUser(), null /* instruction sets */, params.packageAbiOverride,
params.grantedRuntimePermissions);
if (isFwdLocked()) {
throw new IllegalArgumentException("Forward locking only supported in ASEC");
}
}
/** Existing install */
FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets,
null, null);
this.codeFile = (codePath != null) ? new File(codePath) : null;
this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
}
...
}
注意:它的兩個(gè)構(gòu)造函數(shù)通過注釋我們知道绪氛,帶有InstallParams參數(shù)的構(gòu)造函數(shù)是新安裝,而三個(gè)參數(shù)的構(gòu)造函數(shù)則是更新操作的構(gòu)造函數(shù)涝影。
所以他們的關(guān)系如下圖:
同理:AsecInstallArgs類和FileInstallArgs一樣 也有兩個(gè)構(gòu)造函數(shù)枣察,一個(gè)是一個(gè)InstallParams參數(shù)的,用于新安裝燃逻,其中還有一個(gè)多參數(shù)的構(gòu)造函數(shù)序目,用于更新安裝
七、為什么新安裝的情況下 origin.staged等于false
先找到這個(gè)問題的位置伯襟,這個(gè)問題是在handleStartCopy()方法里面涉及到下面代碼:
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
里面的if判斷為false猿涨。
如果想獲取origin.staged,就必須要要知道origin是什么時(shí)候初始化的姆怪。我們知道了origin是在發(fā)送what值為INIT_COPY的Message的時(shí)候初始化的
代碼在PackageManagerService里面的installPackageAsUser方法里面:
代碼在PackageManagerService.java 9569行
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
由于OriginInfo是PackageManagerService的內(nèi)部類叛赚,我們直接找到OriginInfo的fromUntrustedFile靜態(tài)方法
代碼在PackageManagerService.java 10408行
static OriginInfo fromUntrustedFile(File file) {
return new OriginInfo(file, null, false, false);
}
我們看到fromUntrustedFile方法直接new了一個(gè)OriginInfo對(duì)象,而OriginInfo就一個(gè)構(gòu)造函數(shù)稽揭,我們來看下構(gòu)造函數(shù)俺附。
代碼在PackageManagerService.java 10424行
private OriginInfo(File file, String cid, boolean staged, boolean existing) {
this.file = file;
this.cid = cid;
this.staged = staged;
this.existing = existing;
if (cid != null) {
resolvedPath = PackageHelper.getSdDir(cid);
resolvedFile = new File(resolvedPath);
} else if (file != null) {
resolvedPath = file.getAbsolutePath();
resolvedFile = file;
} else {
resolvedPath = null;
resolvedFile = null;
}
}
我們看到staged對(duì)應(yīng)的第三個(gè)入?yún)ⅲ@個(gè)new OriginInfo(file, null, false, false)方法中第三個(gè)參數(shù)是false淀衣。所以我們說如果在新安裝的情況下origin.staged等于false
八昙读、LocalSocket的跨進(jìn)程通信
(一)、Socket
Socket最初用于基于TCP/IP網(wǎng)絡(luò)間進(jìn)程通信中膨桥,以客戶端/服務(wù)器模式進(jìn)行通信蛮浑。實(shí)現(xiàn)異步操作唠叛,共享資源集中處理,提高客戶端響應(yīng)能力
socketAPI 原本是未網(wǎng)絡(luò)通訊設(shè)計(jì)的沮稚,但后來在socket的框架上發(fā)展處一種IPC機(jī)制艺沼,就是UNIX Demain Socket。雖然網(wǎng)絡(luò)socket也可用于同一臺(tái)主機(jī)的進(jìn)程間通信(通過loopback地址127.0.0.1)蕴掏,但是UNIX Demain Socket 用于IPC更有效率:不需要經(jīng)過網(wǎng)絡(luò)協(xié)議棧障般,不需要打包拆包、計(jì)算校驗(yàn)和盛杰、維護(hù)序列號(hào)和應(yīng)答等等挽荡,只是將應(yīng)用層數(shù)據(jù)從一個(gè)進(jìn)程寶貝到另一個(gè)進(jìn)程。這是因?yàn)榧垂琁PC機(jī)制本質(zhì)上是可靠的通信定拟,而網(wǎng)絡(luò)協(xié)議是為不可靠的通訊設(shè)計(jì)的。UNIX Demain Socket也提供面向流和面向數(shù)據(jù)包兩種API接口逗嫡,類似于TCP和UDP青自,但是面向消息的UNIX Domain Socket也是可靠的,消息既不會(huì)丟失也不會(huì)順序錯(cuò)亂驱证。
UNIX Domain Socket是全雙工的延窜,API接口語義豐富,相比其他IPC機(jī)制有明顯的優(yōu)越性抹锄,目前已成為使用最廣泛的IPC機(jī)制逆瑞,比如Window服務(wù)器和GUI程序之間就是通過UNIX Domain Socket通訊的。
(二)祈远、Android的進(jìn)程間通信
我們知道呆万,Android上常見的進(jìn)程間通信有以下幾種情況:
- AIDL進(jìn)程通信接口
- Binder進(jìn)程通信接口
- Message通信框架
- Messager通信框架
- BroadCastReciever廣播
- ContentProvider
其實(shí)還有一種方案上就是基于Unix進(jìn)程通信的LocalSocket
(三)商源、LocalSocket的相關(guān)結(jié)構(gòu)
如下圖:
里面涉及幾個(gè)概念
- LocalSocket:客戶端的套接字车份,在Unix域名空間創(chuàng)建的一個(gè)套接字,是對(duì)Linux中Socket進(jìn)行了封裝牡彻,采用JNI方式調(diào)用扫沼,實(shí)現(xiàn)進(jìn)程間通信。具體就是Native層Server和Framework層Client進(jìn)行通信庄吼,或在各層次中能使用Client/Server模式實(shí)現(xiàn)通信
- LocalSocketAddress:套接字地址缎除,其實(shí)就是文件描述符(主要是服務(wù)器地址,當(dāng)然也可以客戶端自己綁定地址)
- LocalServerSocket:服務(wù)端的套接字总寻,與LocalSocket相對(duì)應(yīng)器罐,創(chuàng)建套接字同時(shí)制定文件描述符
- LocalSocketImpl:Framework層Socket的實(shí)現(xiàn),通過JNI調(diào)用系統(tǒng)socket API
- JNI訪問接口:frameworks/base/core/jni/android_net_LocalSocketImpl.cpp)里面幾個(gè)核心方法
- socket_connect_local
- socket_bind_local
- socket_listen
看下這幾個(gè)類的對(duì)應(yīng)關(guān)系渐行,如下圖:
使用Android的LocalSocket建立socket通信轰坊,是基于網(wǎng)絡(luò)socket過程一致的铸董。
九、createInstallArgs(InstallParams)方法解答
先看下這個(gè)方法在哪里被調(diào)用了肴沫?
是在handleStartCopy()方法里面被調(diào)用
代碼在PackageManagerService.java 10669行
final InstallArgs args = createInstallArgs(this);
我們來看下方法內(nèi)部的執(zhí)行
代碼在PackageManagerService.java 10669行
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
這里面分別根據(jù)move字段和installOnExternalAsec方法來進(jìn)入不同分支來進(jìn)行分支判斷
那我們一個(gè)一個(gè)來判斷粟害,我們看下InstallParams的move的值
1、判斷params.move是否為null
這時(shí)候我們要看下InstallParams的初始化地方在在PackageManagerService的installPackageAsUser()方法里面
代碼在PackageManagerService.java 9572行
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
大家注意下InstallParams的參數(shù)颤芬,下面我們來看下InstallParams的構(gòu)造函數(shù)
InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, String volumeUuid,
VerificationParams verificationParams, UserHandle user, String packageAbiOverride,
String[] grantedPermissions) {
super(user);
this.origin = origin;
this.move = move;
this.observer = observer;
this.installFlags = installFlags;
this.installerPackageName = installerPackageName;
this.volumeUuid = volumeUuid;
this.verificationParams = verificationParams;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
}
其中我們看到第二個(gè)參數(shù)是對(duì)應(yīng)的move字段悲幅,而在new InstallParams對(duì)象的時(shí)候,我看到第二個(gè)參數(shù)是null站蝠。而在后續(xù)的整個(gè)流程汰具,并沒有給這move字段賦值,所以params.move等于null
結(jié)論:params.move等于null菱魔。
2郁副、判斷installOnExternalAsec(params.installFlag)和params.isForwardLocked()的值
首先我們來看下params.installFlag的值,通過上面InstallParams的值我們知道InstallParams的installFlag其實(shí)在構(gòu)造InstallParams的時(shí)候豌习,傳入的變量installFlags存谎,那我們向前捋捋,看看這個(gè)這個(gè)installFlags是什么時(shí)候初始化的肥隆,后續(xù)是否有發(fā)生什么值變化既荚。我們發(fā)現(xiàn)這個(gè)installFlags其實(shí)是installPackageAsUser()的入?yún)ⅲ俏覀兙驮傧蚯罢?
發(fā)現(xiàn)在InstallAppProgress.java 228行的的initView() 方法里面
public void initView() {
...
int installFlags = 0;
...
}
我們發(fā)現(xiàn)installFlags等于0栋艳,并且"新安裝"的情況下恰聘,是沒有變更installFlags的值的。所以在PackageService的installPackageAsUser方法里面的入?yún)nstallFlags也是0吸占,在進(jìn)入installPackageAsUser里面有變更installFlags的值地方即在PackageManagerService.java 9546行
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
通過代碼我們知道
它顯示先"取反"晴叨,然后依次進(jìn)行位"與"操作。不好意思矾屯。由于篇幅管理兼蕊,我這里就不后續(xù)跟蹤,因?yàn)楦櫟膬?nèi)容太多了件蚕。希望大家理解孙技。最后的結(jié)果是installOnExternalAsec(params.installFlags)是false和params.isForwardLocked()也是false。所以這個(gè)方法最后返回的是FileInstallArgs排作。
十牵啦、isVerificationEnabled(int userId, int installFlags) 的理解
代碼在PackageManagerService.java 9957行
/**
* Check whether or not package verification has been enabled.
*
* @return true if verification should be performed
*/
private boolean isVerificationEnabled(int userId, int installFlags) {
// DEFAULT_VERIFY_ENABLE是個(gè)常量,為true
if (!DEFAULT_VERIFY_ENABLE) {
return false;
}
// 檢查是否是否受限用戶妄痪,如果是受限用戶哈雏,則要進(jìn)行檢查
boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
// Check if installing from ADB
// 如果是 從通過ADB安裝
if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
// Do not run verification in a test harness environment
// 如果是測(cè)試工具,則不用檢查
if (ActivityManager.isRunningInTestHarness()) {
return false;
}
// 如果是受限用戶,則要進(jìn)行檢查
if (ensureVerifyAppsEnabled) {
return true;
}
// Check if the developer does not want package verification for ADB installs
// 如果開發(fā)設(shè)置了在ADB安裝的時(shí)候不需要檢查包裳瘪,則不用檢查
if (android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 0) {
return false;
}
}
// 如果是受限用戶履因,則一定要進(jìn)行包檢驗(yàn)
if (ensureVerifyAppsEnabled) {
return true;
}
return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
}
先翻譯一下注釋:
檢查是否啟用包驗(yàn)證
如果執(zhí)行驗(yàn)證,則返回true
上面的注釋已經(jīng)解釋的很清楚了盹愚,讓我們來看下最后一行代碼
android.provider.Settings.Global.getInt(mContext.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1)
這行的代碼意思如下:
PackageManagerServcie在安裝之前是否發(fā)送廣播以驗(yàn)證應(yīng)用
- 1:表示 如果驗(yàn)證者存在栅迄,則在安裝應(yīng)用之前進(jìn)行包驗(yàn)證,
- 0:表示 安裝器那不要驗(yàn)證應(yīng)用程序
十一皆怕、Context.sendBroadcast(Intent intent)的功能是和Context.sendBroadcastAsUser(Intent,UserHandle)一樣的解答
1毅舆、Context.sendBroadcast(Intent intent)的具體實(shí)現(xiàn)
我們知道Context是一個(gè)抽象類,而具體實(shí)現(xiàn)類是ContextImpl愈腾。所以Context.sendBroadcast(Intent intent)的具體實(shí)現(xiàn)如下:
代碼在ContextImpl.java 762行
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
2憋活、Context. sendBroadcastAsUsersendBroadcastAsUser(Intent, UserHandle)的具體實(shí)現(xiàn)
我們知道Context是一個(gè)抽象類,而具體實(shí)現(xiàn)類是ContextImpl虱黄。所以Context.sendBroadcast(Intent intent)的具體實(shí)現(xiàn)如下:
代碼在ContextImpl.java 923行
@Override
public void sendBroadcastAsUser(Intent intent, UserHandle user) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
其實(shí)大家自己對(duì)比兩個(gè)方法悦即,會(huì)發(fā)現(xiàn),這兩個(gè)方法其實(shí)都是調(diào)用ActivityManagerNative.getDefault().broadcastIntent方法而已橱乱,唯一的不同是辜梳,最后一個(gè)參數(shù)不同:sendBroadcast最后一個(gè)參數(shù)是getUserId(),而sendBroadcastAsUser方法最后一個(gè)參數(shù)是user.getIdentifier()泳叠。
這樣我們?cè)诳聪耮etUserId()方法里面的具體內(nèi)容作瞄,如下圖:
代碼在ContextImpl.java 1770行
/** {@hide} */
@Override
public int getUserId() {
return mUser.getIdentifier();
}
我們發(fā)現(xiàn)getUserId()方法內(nèi)部也是調(diào)用的mUser.getIdentifier(),所以我們說
Context.sendBroadcast(Intent intent)的功能是和Context.sendBroadcastAsUser(Intent,UserHandle)一樣的
十二危纫、Split APK(APK拆分)與Instant Run簡(jiǎn)介
如果想了解官網(wǎng)宗挥,推薦Android官方技術(shù)文檔翻譯——Apk 拆分機(jī)制
(一)、什么是Split APK(APK 拆分)
Split APK是Google為了解決66536上線种蝶,以及APK安裝包越來越大等問題契耿,在Android 5.0中引入的一種機(jī)制。Split APK可以將一個(gè)龐大的APK文件螃征,按屏幕密度搪桂、ABI等形式拆分成多個(gè)獨(dú)立的APK,在應(yīng)用程序更新時(shí)会傲,不必下載整個(gè)APK锅棕,只需要下載某個(gè)某塊即可安裝更新。Split APK 將原來一個(gè)APK中多個(gè)模塊共享一份資源的模型分離成多個(gè)APK使用各自的資源淌山,并且可以繼承Base APK中的資源,多個(gè)APK有相同的data顾瞻、cache目錄泼疑、多個(gè)dex文件、相同的進(jìn)程荷荤,在Settings.apk中只顯示一個(gè)APK退渗,并且使用相同的包名移稳。
如下圖
PS:在Android Studio 2.3上,instant run的部署方案與之前的版本相比有了很大變化会油,之前是通過分dex來實(shí)現(xiàn)動(dòng)態(tài)部署个粱,而從Android Studio 2.3上則是通過Split APK技術(shù)。而在Android Studio 2.2翻翩,只有部署到Android Studio 6.0以上的設(shè)備才會(huì)使用Split APK 方案都许。 Android Studio 2.3則是連Android 5.0都會(huì)使用Split APK。在安裝時(shí)會(huì)通過adb install-multiple 指令一次性安裝嫂冻。
(二)胶征、Splite APK效果圖及解析
通俗的理解桨仿,之前我們是一個(gè)APK睛低,而現(xiàn)在是通過Splite APK,則在我們的APK安裝目錄下有多個(gè)APK服傍。如下圖:
看上圖钱雷,一個(gè)外殼base.apk,一個(gè)依賴split_lib_dependencies_apk吹零,然后將我們的業(yè)務(wù)代碼分成了10份急波,其中base.apk中的dex只包含了instant-run-server的代碼以及我們?cè)贏ndroidManifest、資源文件等瘪校。查看這些分割的APK文件澄暮,我們會(huì)發(fā)現(xiàn)里面只有一個(gè)dex、AndroidManifest和mf文件夾阱扬。打開AndroidManifest文件泣懊,僅有一個(gè)manifest標(biāo)簽,然后有個(gè)split屬性麻惶。而base.apk的manifest文件是沒有這個(gè)屬性的馍刮。從安裝的源碼可以看出,安裝時(shí)必須要有一個(gè)apk是沒有這個(gè)屬性的窃蹋,這個(gè)就是base.apk
整個(gè)原理就是比較簡(jiǎn)單了卡啰,生成多個(gè)apk文件,把資源文件警没、manifest等放到base.apk匈辱,然后把業(yè)務(wù)dex分散到其他apk去,并且加入一個(gè)空的manifest文件杀迹,并指定split屬性亡脸。在打包的過程中,業(yè)務(wù)dex不再打入主apk,而是和各自的manifest文件打包成新的apk浅碾。
要說Split APK就不得不說下 Instant Run大州,我們?cè)谶@里簡(jiǎn)單的介紹下Instant Run
(三)、Instant Run簡(jiǎn)介
1垂谢、 Instant Run 介紹
Instant Run厦画,是android studio 2.0新增的一個(gè)運(yùn)行機(jī)制,在編碼開發(fā)滥朱、測(cè)試或debug的時(shí)候根暑,它能顯著減少你對(duì)當(dāng)前APP"構(gòu)建"和"部署"的時(shí)間。當(dāng)我們第一次點(diǎn)擊run、debug按鈕的時(shí)候,它運(yùn)行時(shí)間和我們平常一樣厨诸,但是在后續(xù)的流程中浑此,你每次修改代碼后,點(diǎn)擊run、debug按鈕,對(duì)應(yīng)的"改變"將迅速的部署到你正在運(yùn)行的程序上,速度超級(jí)快薇芝。
2、產(chǎn)生Instant Run的背景
在沒有Instant Run的時(shí)候丰嘉,我們一般修改代碼夯到,然后點(diǎn)擊"run"的流程如此:構(gòu)建->部署->安裝->app登錄->activity創(chuàng)建
如下圖:
每一次都是重新安裝,但是這樣會(huì)導(dǎo)致大量的時(shí)間花在"構(gòu)建->部署->安裝->app登錄->activity創(chuàng)建"上饮亏,這樣就產(chǎn)生了一個(gè)需求耍贾,能否縮短這個(gè)時(shí)間。所有就有了Instant Run
Instant Run產(chǎn)生的目的就是: 盡可能多的剔除不必要的流程路幸,然后提升必要的流程的效率荐开,從而縮短時(shí)間。
結(jié)合上圖的流程简肴,大家想一下晃听,怎樣才縮短時(shí)間?
只對(duì)代碼改變部分做構(gòu)建和部署砰识,并不重新安裝應(yīng)用能扒,并不重啟應(yīng)用,不重啟Activity辫狼,就就會(huì)大大縮短時(shí)間初斑。
3、 Instant Run的分類
按照是否需要重啟當(dāng)前Activity予借、是否需要重啟APP(不是重新安裝)這兩個(gè)條件越平,把Instant Run分為3類:
- Hot Swp——熱插拔:
改變的代碼被應(yīng)用投射到APP上频蛔,不需要重啟應(yīng)用灵迫,不需要重新啟動(dòng)當(dāng)前Activity秦叛。
一般適用簡(jiǎn)單的改變的場(chǎng)景,比如一些方法簡(jiǎn)單的修改等- Warm Swap——溫插拔:
Activity需要被重啟才能看到所需修改
一般適用涉及到了資源文件的修改瀑粥,比如Resources挣跋。- Cold Swap——冷插拔:
APP需要被重啟,這里說的重啟狞换,并不是重新安裝避咆。
一般適用結(jié)構(gòu)性變化的場(chǎng)景,比如修改了繼承規(guī)則等修噪。
如下圖:
4查库、Instant Run的原理
Manifest整合,然后跟res黄琼、dex.file一起被合并到APK
manifest文件合并樊销、打包,和res一起被AAPT合并到APK中脏款,同樣項(xiàng)目代碼被編譯成字節(jié)碼围苫,然后轉(zhuǎn)換成.dex文件,也被合并到APK中撤师。
下面我們來看下首次運(yùn)行Instant Run剂府,Gradle執(zhí)行的操作。
在有Instant Run的環(huán)境下:一個(gè)新的App Server類會(huì)被注入到App中剃盾,與Bytecode instrumentation協(xié)同監(jiān)控代碼的變化腺占。同時(shí)會(huì)有一個(gè)新的Application類,它注入了一個(gè)自定義類加載器(Class Loader)痒谴,同時(shí)該Application類會(huì)啟動(dòng)我們所需要的新注入的App Server衰伯。于是Manifest會(huì)被修改來確保我們的應(yīng)用能使用這個(gè)新的Application類(這里不比擔(dān)心自己繼承定義了Application類,Instant Run添加的這個(gè)新Application類會(huì)代理我們自定義的Application類)闰歪。至此Instant Run可以跑起來了嚎研,在我們使用的時(shí)候,它會(huì)通過決策库倘,合理運(yùn)用熱溫冷插拔來協(xié)助我們大量地縮短構(gòu)建程序時(shí)間临扮。