应用程序安装流程

24 篇文章 2 订阅
订阅专栏

https://maoao530.github.io/2017/01/18/package-install/

 

本文介绍APK的安装流程。

一、安装流程图

APK安装流程,总体可以下图流程,用ProcessOn画的,凑合看:

package_install

从上图我们可以看到apk安装到最后都会调用到这个flow:

PMS.scanPackageTracedLI => PMS.scanPackageLI => PMS.scanPackageDirtyLI

关于这个flow,之前的博客有详细介绍过,本文不再展开 :  https://maoao530.github.io/2017/01/10/packagemanager/

后续的博文会根据这张图展开说明。

二、APK文件结构

APK(Android Package),可以看做是一个zip压缩包,可以通过解压缩工具解开,其文件结构如下:

目录 or 文件描述
assert存放的原生资源文件,通过AssetManager类访问
libnative库文件
META-INF存放签名信息,用来保证APK包的完整性和系统的安全。系统安装APK时,应用管理器会按照对应算法对包里文件做校验,如果校验结果与META-INF中内容不一致,则不会安装这个APK。
res种资源文件系统会在R.java里面自动生成该资源文件的ID,所以访问这种资源文件比较简单,通过R.XXX.ID即可
AndroidManifest.xml每个应用都必须定义和包含,描述应用的名字、版本权限、引用的库文件等信息。apk中的AndroidManifest.xml经过压缩,可以通过AXMLPrinter2工具解开。
classes.dex是JAVA源码编译后生成的JAVA字节码文件。但Android使用的dalvik虚拟机与标准的JAVA虚拟机不兼容,dex文件与class文件相比,不论是文件结构还是opcode都不一样。
resources.arsc编译后的二进制资源文件。

三、APK安装方法

APK有下面4种安装方法:

方法描述
开机过程中安装开机时完成,没有安装界面,如系统应用、其它预置应用
adb工具安装没有安装界面,adb install/push xxxx.apk
第三方应用安装通过packageinstaller.apk进行安装,有安装界面,如打开文件管理器并点击sdk卡里APK文件
网络下载应用安装通过google market应用完成,没有安装界面

简单说明下apk安装的基本过程:

  • 拷贝目标apk到指定文件目录
  • 调用scanPackageLI为apk文件在系统中注册信息

四、应用程序安装过程

上述几种安装方法最终都通过PackageManagerService.scanPackageLI完成,总结起来大致有以下三种方式:

  • adb push:
    PackageManagerService的内部类AppDirObserver实现了监听app目录的功能,当把某个APK文件放到app目录下面时,PMS会收到ADD_EVENTS事件
    frameworks\base\services\java\com\android\server\pm\PackageManagerService.java

  • adb install:
    安装入口函数为Pm.runInstall
    frameworks\base\cmds\pm\src\com\android\commands\pm\Pm.java

  • 网络下载应用安装和第三方应用安装:
    安装入口函数为ApplicationPackageManager.installPackage
    frameworks\base\core\java\android\app\ApplicationPackageManager.java

接下来我们来分别详细说明这些安装流程:

五、adb push

Android 4.4平台,PackageManagerService的内部类AppDirObserver实现了监听app目录的功能,当把某个APK文件放到app目录下面时,PMS会收到ADD_EVENTS事件。
如果是添加事件,则调用scanPackageLI,并使用updatePermissionsLPw授权;如果是删除事件则调用removePackageLI移除该apk的相关信息。最后都要调用writeLPr重新保存相关信息到packages.xml。

关于AppDirObserver具体如何监听的,可以查看: AppDirObserver

不过我在android 7.0 sdk里面没有看到这个类,难道7.0把这个功能砍了?手头没有7.0平台,不好验证。

我猜测现在通过adb push apk到data/app或者system/app的apk,如果这个监听的功能砍了,那么应该是会通过reboot重启系统,走PMS.main流程,scanDir–>scanPackageLI去安装apk。

以上待填坑。

六、adb install

adb install 的安装方式,会调用system/core/adb/commandline.cpp中的adb_commandline函数:

 

1

2

3

4

5

 

adb_commandline

install_app_legacy or install_app

pm_command

send_shell_command

Pm.runInstall()

 

这个过程会把apk文件copy到data/local/tmp/目录下,然后向shell服务发送pm命令安装apk,最后调用Pm.runInstall()方法来安装apk。

6.1 pm.runInstall

frameworks\base\cmds\pm\src\com\android\commands\pm\Pm.java

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

 

private int runInstall() throws RemoteException {

final InstallParams params = makeInstallParams();

// 1. 创建session

final int sessionId = doCreateSession(params.sessionParams,

params.installerPackageName, params.userId);

try {

final String inPath = nextArg();

if (inPath == null && params.sessionParams.sizeBytes == 0) {

System.err.println("Error: must either specify a package size or an APK file");

return 1;

}

// 2. 写session

if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",

false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {

return 1;

}

// 3. 提交Session

if (doCommitSession(sessionId, false /*logSuccess*/)

!= PackageInstaller.STATUS_SUCCESS) {

return 1;

}

System.out.println("Success");

return 0;

} finally {

try {

mInstaller.abandonSession(sessionId);

} catch (Exception ignore) {

}

}

}

从上面的代码来看,runInstall主要进行了三件事,即创建session、对session进行写操作,最后提交session。

6.1.1 doCreateSession

实际调用的是PackageInstallerService的createSession,这个过程主要是为APK安装做好准备工作,例如权限检查、目的临时文件的创建等, 最终创建出PackageInstallerSession对象。PackageInstallerSession可以看做是”安装APK”这个请求的封装,其中包含了处理这个请求需要的一些信息。
实际上PackageInstallerSession不仅是分装请求的对象,其自身还是个服务端。

6.1.2 doWriteSession

通过PackageInstallerSession将/data/local/tmp的apk拷贝到终端目录内。

6.1.3 doCommitSession

doWriteSession结束后,如果没有出现任何错误,那么APK源文件已经copy到目的地址了,doCommitSession最终会调用到PMS.installStage来安装apk,调用流程如下:

PackageInstallerSession.commit ==> commitLocked(); ==> PMS.installStage()

PMS.installStage()会调用sendMessage将”INIT_COPY”发送给PackageHandler:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

 

void installStage(String packageName, File stagedDir, String stagedCid,

IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,

String installerPackageName, int installerUid, UserHandle user,

Certificate[][] certificates) {

if (DEBUG_EPHEMERAL) {

if ((sessionParams.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {

Slog.d(TAG, "Ephemeral install of " + packageName);

}

}

final VerificationInfo verificationInfo = new VerificationInfo(

sessionParams.originatingUri, sessionParams.referrerUri,

sessionParams.originatingUid, installerUid);

final OriginInfo origin;

if (stagedDir != null) {

origin = OriginInfo.fromStagedFile(stagedDir);

} else {

origin = OriginInfo.fromStagedContainer(stagedCid);

}

final Message msg = mHandler.obtainMessage(INIT_COPY);

final InstallParams params = new InstallParams(origin, null, observer,

sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,

verificationInfo, user, sessionParams.abiOverride,

sessionParams.grantedRuntimePermissions, certificates);

params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));

msg.obj = params;

Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",

System.identityHashCode(msg.obj));

Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",

System.identityHashCode(msg.obj));

mHandler.sendMessage(msg);

}

PackageHandler用于处理apk的安装请求等消息,后面分析。

七、ApplicationPackageManager

网络下载应用安装或者通过第三方应用安装,最终都会通过ApplicationPackageManager.installPackage来安装:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

 

public void installPackage(Uri packageURI, PackageInstallObserver observer,

int flags, String installerPackageName) {

installCommon(packageURI, observer, flags, installerPackageName, mContext.getUserId());

}

private void installCommon(Uri packageURI,

PackageInstallObserver observer, int flags, String installerPackageName,

int userId) {

if (!"file".equals(packageURI.getScheme())) {

throw new UnsupportedOperationException("Only file:// URIs are supported");

}

final String originPath = packageURI.getPath();

try {

// PMS.installPackageAsUser

mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,

userId);

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

PMS.installPackageAsUser调用sendMessage将”INIT_COPY”发送给PackageHandler:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 

@Override

public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,

int installFlags, String installerPackageName, int userId) {

...

final Message msg = mHandler.obtainMessage(INIT_COPY);

final VerificationInfo verificationInfo = new VerificationInfo(

null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);

final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,

installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,

null /*packageAbiOverride*/, null /*grantedPermissions*/,

null /*certificates*/);

params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));

msg.obj = params;

mHandler.sendMessage(msg);

....

}

PackageHandler用于处理apk的安装请求等消息,后面分析。

八、PackageHanlder

  • PMS.installStage()会调用sendMessage将”INIT_COPY”发送给PackageHandler
  • PMS.installPackageAsUser调用sendMessage将”INIT_COPY”发送给PackageHandler

8.1 INIT_COPY

PackageHandler用于处理apk的安装请求等消息,在PMS构造函数中有初始化。实际处理消息的函数为doHandleMessage,我们来看看INIT_COPY的处理流程:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

 

class PackageHandler extends Handler {

...

void doHandleMessage(Message msg) {

switch (msg.what) {

case INIT_COPY: {

//这里取出的其实就是InstallParams

HandlerParams params = (HandlerParams) msg.obj;

//idx为当前等待处理处理的安装请求的个数

int idx = mPendingInstalls.size();

............

//初始时,mBound的值为false

if (!mBound) {

............

// If this is the only one pending we might

// have to bind to the service again.

//连接安装服务

if (!connectToService()) {

..................

} else {

// Once we bind to the service, the first

// pending request will be processed.

//绑定服务成功后,将新的请求加入到mPendingIntalls中,等待处理

mPendingInstalls.add(idx, params);

}

} else {

//如果之前已经绑定过服务,同样将新的请求加入到mPendingIntalls中,等待处理

mPendingInstalls.add(idx, params);

// Already bound to the service. Just make

// sure we trigger off processing the first request.

if (idx == 0) {

//如果是第一个请求,则直接发送事件MCS_BOUND,触发处理流程

mHandler.sendEmptyMessage(MCS_BOUND);

}

}

break;

}

}

}

...

}

INIT_COPY主要是将新的请求加入到mPendingIntalls中,等待MCS_BOUND阶段处理。

8.2 MCS_BOUND

INIT_COPY最后会发送MCS_BOUND消息触发接下来的流程,MCS_BOUND对应的处理流程同样定义于doHandleMessage中:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

 

void doHandleMessage(Message msg) {

.......

case MCS_BOUND: {

........

if (msg.obj != null) {

mContainerService = (IMediaContainerService) msg.obj;

.......

}

if (mContainerService == null) {

if (!mBound) {

// Something seriously wrong since we are not bound and we are not

// waiting for connection. Bail out.

............

} else {

Slog.w(TAG, "Waiting to connect to media container service");

}

// 请求队列mPendingInstalls不为空

} else if (mPendingInstalls.size() > 0) {

HandlerParams params = mPendingInstalls.get(0);

if (params != null) {

........

//调用参数的startCopy函数处理安装请求

if (params.startCopy()) {

........

// Delete pending install

if (mPendingInstalls.size() > 0) {

mPendingInstalls.remove(0);

}

if (mPendingInstalls.size() == 0) {

if (mBound) {

..........

removeMessages(MCS_UNBIND);

Message ubmsg = obtainMessage(MCS_UNBIND);

// Unbind after a little delay, to avoid

// continual thrashing.

sendMessageDelayed(ubmsg, 10000);

}

} else {

// There are more pending requests in queue.

// Just post MCS_BOUND message to trigger processing

// of next pending install.

......

mHandler.sendEmptyMessage(MCS_BOUND);

}

}

.........

}

} else {

// Should never happen ideally.

Slog.w(TAG, "Empty queue");

}

break;

}

.......

}

这一段代码比较好理解:

  • 如果mPendingInstalls不为空,调用InstallParams.startCopy函数处理安装请求。
  • 接着如果mPendingInstalls不为空,发送MCS_BOUND继续处理下一个,直到队列为空。
  • 如果队列为空,则等待一段时间后,发送MCS_UNBIND消息断开与安装服务的绑定。

九、startCopy

/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

InstallParams继承HandlerParams,实际调用的是HandlerParams.startCopy:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

 

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()处理

handleStartCopy();

Slog.i(TAG, "Apk copy done");

res = true;

}

} catch (RemoteException e) {

if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");

mHandler.sendEmptyMessage(MCS_RECONNECT);

res = false;

}

//

handleReturnCode();

return res;

}

PMS将先后调用handleStartCopy和handleReturnCode来完成主要的工作。

9.1 handleStartCopy

handleStartCopy函数在HandleParams抽象类定义,在其子类InstallParams来实现,我们看看与实际安装相关的handleStartCopy函数:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

 

public void handleStartCopy() throws RemoteException {

int ret = PackageManager.INSTALL_SUCCEEDED;

// 决定是安装在手机内还是sdcard中,设置对应标志位

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");

}

}

...

// 检查APK的安装位置是否正确

if (onInt && onSd) {

// Check if both bits are set.

Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");

ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;

} else if (onSd && ephemeral) {

Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");

ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;

} else {

...

}

...

// createInstallArgs用于创建一个安装参数对象

final InstallArgs args = createInstallArgs(this);

if (ret == PackageManager.INSTALL_SUCCEEDED) {

...

// 调用InstallArgs的copyApk函数

ret = args.copyApk(mContainerService, true);

}

}

mRet = ret;

}

InstallParams$handleStartCopy()主要功能是获取安装位置信息以及复制apk到指定位置。抽象类InstallArgs中的copyApk负责复制APK文件,具体实现在子类FileInstallArgs和SdInstallArgs里面。

9.2 handleReturnCode

InstallParams$handleReturnCode()中,调用processPendingInstall方法处理安装:

 

1

2

3

4

5

6

7

8

 

void handleReturnCode() {

// If mArgs is null, then MCS couldn't be reached. When it

// reconnects, it will try again to install. At that point, this

// will succeed.

if (mArgs != null) {

processPendingInstall(mArgs, mRet);

}

}

9.3 processPendingInstall

主要的安装流程都在这个方法里面: PMS.processPendingInstall

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

 

private void processPendingInstall(final InstallArgs args, final int currentStatus) {

mHandler.post(new Runnable() {

public void run() {

mHandler.removeCallbacks(this);

// Result object to be returned

PackageInstalledInfo res = new PackageInstalledInfo();

res.setReturnCode(currentStatus);

res.uid = -1;

res.pkg = null;

res.removedInfo = null;

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

//1、预安装,检查包状态,确保环境ok,如果环境不ok,那么会清理拷贝的文件

args.doPreInstall(res.returnCode);

synchronized (mInstallLock) {

//2、安装,调用installPackageTracedLI进行安装

installPackageTracedLI(args, res);

}

//3、安装收尾

args.doPostInstall(res.returnCode, res.uid);

}

if (!doRestore) {

.......

//4、生成一个POST_INSTALL消息给PackageHanlder

Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);

mHandler.sendMessage(msg);

}

}

});

}

安装过程放在一个线程里面,处理流程是预安装-安装-安装收尾-发送 POST_INSTALL消息:

  • 预安装:检查当前安装包的状态以及确保SDCARD的挂载,并返回状态信息。在安装前确保安装环境的可靠。
  • 安装:对mInstallLock加锁,表明同时只能有一个安装包进行安装;然后调用installPackageTracedLI完成具体安装操作。
  • 安装收尾: 检查状态,如果安装不成功,删除掉相关目录文件。
  • 发送POST_INSTALL消息:该消息由PackageHandler接收。POST_INSTALL的主要工作其实还是通过广播、回调接口通知系统中的其它组件,有新的Pacakge安装或发生了改变。

从上面我们可以知道,具体安装apk的函数是PMS.installPackageTracedLI

十、installPackageTracedLI

PMS.installPackageTracedLI函数:

 

1

2

3

4

5

6

7

8

 

private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {

try {

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");

installPackageLI(args, res);

} finally {

Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

}

}

十一、installPackageLI

继续PMS.installPackageLI:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

 

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {

// PackageParser对象

PackageParser pp = new PackageParser();

pp.setSeparateProcesses(mSeparateProcesses);

pp.setDisplayMetrics(mMetrics);

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");

final PackageParser.Package pkg;

try {

if (DEBUG_INSTALL) Slog.i(TAG, "Start parsing apk: " + installerPackageName);

// 1.开始解析我们的package

pkg = pp.parsePackage(tmpPackageFile, parseFlags);

if (DEBUG_INSTALL) Slog.i(TAG, "Parsing done for apk: " + installerPackageName);

} catch (PackageParserException e) {

res.setError("Failed parse during installPackageLI", e);

return;

} finally {

Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

}

...

//2. 加载证书,获取签名信息

try {

// either use what we've been given or parse directly from the APK

if (args.certificates != null) {

try {

PackageParser.populateCertificates(pkg, args.certificates);

} catch (PackageParserException e) {

PackageParser.collectCertificates(pkg, parseFlags);

}

} else {

PackageParser.collectCertificates(pkg, parseFlags);

}

} catch (PackageParserException e) {

res.setError("Failed collect during installPackageLI", e);

return;

}

...

synchronized (mPackages) {

// 3.检测packages是否存在

if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {

...

replace = true;

} else if (mPackages.containsKey(pkgName)) {

...

replace = true;

if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);

}

...

}

}

...

try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,

"installPackageLI")) {

if (replace) {

// 4.更新已经存在的packages

replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,

installerPackageName, res);

} else {

// 5.安装新的packages

installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,

args.user, installerPackageName, volumeUuid, res);

}

}

...

}

这个函数过程比较长,主要做了几件事:

  • PackageParser$parsePackage,主要是解析APK的AndroidManifest.xml,将每个标签对应的信息添加到Package的相关列表中,如将下的信息添加到Package的activities列表等。
  • 加载apk证书,获取签名信息
  • 检查目前安装的APK是否在系统中已存在:
    • 已存在,则调用replacePackageLIF进行替换安装。
    • 不存在,否则调用installNewPackageLIF进行安装。

11.1 replacePackageLIF

如果需要替换的是系统APP,则调用Settings$disableSystemPackageLPw来disable旧的APK;如果替换的是非系统APP,则调用deletePackageLI删除旧的APK。

因为这个过程实在太差,没有必要贴出来一一分析,我来简化一下flow,有兴趣的读者可以深入跟进:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

 

replacePackageLIF

replaceSystemPackageLIF // 系统 pkg

removePackageLI

disableSystemPackageLPw

clearAppDataLIF

scanPackageTracedLI //安装apk

scanPackageLI

scanPackageDirtyLI

updateSettingsLI

updatePermissionsLPw

mSettings.writeLPr();

replaceNonSystemPackageLIF // 非系统 pkg

deletePackageLIF

clearAppDataLIF

clearAppProfilesLIF

scanPackageTracedLI // 安装apk

scanPackageLI

scanPackageDirtyLI

updateSettingsLI

updatePermissionsLPw

mSettings.writeLPr();

不管是更新系统还是非系统apk,都会先清除之前的packages信息,然后通过scanPackageTracedLI去安装apk,安装完后更新permissions和setting,最后通过writeLPr更新packages.xml。

关于scanPackageTracedLI和Settings.writeLPr();我有在上一篇blog讲过,可以回去看看。

11.2 installNewPackageLIF

PMS.installNewPackageLIF用于安装新的apk:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

 

private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,

int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,

PackageInstalledInfo res) {

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");

// Remember this for later, in case we need to rollback this install

String pkgName = pkg.packageName;

if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);

// package已经存在

synchronized(mPackages) {

if (mSettings.mRenamedPackages.containsKey(pkgName)) {

// A package with the same name is already installed, though

// it has been renamed to an older name. The package we

// are trying to install should be installed as an update to

// the existing one, but that has not been requested, so bail.

res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName

+ " without first uninstalling package running as "

+ mSettings.mRenamedPackages.get(pkgName));

return;

}

if (mPackages.containsKey(pkgName)) {

// Don't allow installation over an existing package with the same name.

res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName

+ " without first uninstalling.");

return;

}

}

try {

// 1. 安装apk

PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,

System.currentTimeMillis(), user);

// 2. 更新setting

updateSettingsLI(newPackage, installerPackageName, null, res, user);

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {

prepareAppDataAfterInstallLIF(newPackage);

} else {

// Remove package from internal structures, but keep around any

// data that might have already existed

deletePackageLIF(pkgName, UserHandle.ALL, false, null,

PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);

}

} catch (PackageManagerException e) {

res.setError("Package couldn't be installed in " + pkg.codePath, e);

}

Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

}

installNewPackageLIF会调用scanPackageTracedLI去安装apk,最终会调用scanPackageLI->scanPackageDirtyLI实际去安装apk。

由于之前有描述过,便不再叙述

Android PMS——应用安装流程介绍(五)
小旭的专栏
02-01 306
从 PMS(Package Management System)源码的角度来分析应用程序安装流程,涉及以下主要步骤:获取 APK 文件:在应用程序安装之前,需要先获取 APK 文件。APK 文件是 Android 应用程序安装包,它包含了应用程序的代码和资源文件。解析 APK 文件:PMS 首先需要对 APK 文件进行解析,以获取应用程序的信息和组件信息,例如应用程序包名、版本号、权限列表、组件列表(如 Activity、Service、Receiver 等)等。
WEB应用程序安装程序制作教程
06-25
WEB应用程序安装程序制作教程,叫你如何打包Web应用程序,制作安装包,好用的文档哦
实训报告8驱动程序和应用程序安装实训报告.docx
06-24
实训报告8驱动程序和应用程序安装实训报告
北京CA 证书应用安装程序
07-25
北京CA 证书应用安装程序
大体了解Android应用安装过程及原理
Jason_Lee155的博客
03-24 3279
Android应用安装流程及路径: 应用安装涉及到如下几个目录: system/app 系统自带的应用程序,无法删除 data/app 用户程序安装的目录,有删除权限。安装时把apk文件复制到此目录 data/data 存放应用程序的数据 Data/dalvik-cache 将apk...
android应用安装流程详解
allen_xu_2012_new的博客
05-15 4367
在InstallingAsyncTask的doInBackground()里会根据包的Uri,将APK的信息通过IO流的形式写入到PackageInstaller.Session中,最后会在onPostExecute()中调用PackageInstaller.Session的commit方法,进行安装。在上述的过程中,通过PackageInstaller维持了Session,把安装包写入到Session,真正的安装过程就要来看PMS了。在这里就通过文件流的操作,把Apk拷贝到/data/app的目录下了。
androidP: apk安装流程
zhuowalun8427的博客
12-28 5533
一、Android应用安装有如下四种方式 1 系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面。 4. 第三方应用安装――通过SD卡里的APK文件安装,有安装界面,由packageinstaller.apk应用处理安装及卸载过程的界面。 二、安装相关 1、应用安装流程及路径 (1)应用安装涉及到如下几个目录: system/app 系统自带的应用程序,无法删除 (2)data/app 用户程序安装的目录,有删除权限
2024最版Android逆向教程——第2天:dex反编译工具的安装和使用
热门推荐
Amo Xiang的博客
10-09 1万+
学习了 dex 反编译工具的安装和使用,利用此一类工具,我们可以非常方便地反编译 apk 文件,还原出原始的 Java 代码,从而找到我们想要的核心逻辑。
一个应用程序APP安装到手机上时发生了什么变化
yzwfeng的专栏
11-30 282
安装和卸载都是通过PackageManager,实质上是实现了PackageManager的远程服务PackageManagerService来完成具体的操作,所有细节和逻辑均可以在PackageManagerService中跟踪查看;所有安装方式殊途同归,最终就回到PackageManagerService中,然后调用底层本地代码的install来完成。
Android PackageManagerService总结(四) APK安装流程
u012514113的专栏
03-25 3856
管理本地apk,主要包括安装、删除 本篇文章重点介绍一下apk安装流程
Android编程判断应用程序是否已安装的方法
09-02
主要介绍了Android编程判断应用程序是否已安装的方法,实例分析了Android基于PackageInfo判断应用程序信息的相关技巧,需要的朋友可以参考下
使用JavaScript的ActiveXObject对象检测应用程序是否安装的方法
10-26
主要介绍了使用JavaScript的ActiveXObject对象检测应用程序是否安装的方法,需要的朋友可以参考下
计算机软件的安装步骤及注意事项,组态软件介绍以及安装注意事项
weixin_39909212的博客
07-15 1108
Siemens 官方网站提供 WinCC flexible Smart V3SP2软件下载的地址:安装之前请注意操作系统兼容性以及下述注意事项:安装之前一定要确认操作系统兼容性,不能在不兼容的操作系统上安装组态软件。并且操作系统必须是完整版,不建议使用ghost版本以及番茄花园等一键安装版。本例使用Windows 10 1709 专业版演示如何安装Smart V3 SP2软件:1.右键点击下载的W...
虚拟机安装相关软件流程
2301_76557773的博客
08-24 1796
(1)编辑命令vi 文件名(1)使用vi命令时---->命令行模式---->i---->编辑模式-----esc[键]--->命令行模式--->【:qw退出并保存】【:q!强制退出】修改我们虚拟机的ip地址。---如果不改,那么centos默认动态ip.重启动网络 systemctl restart network测试: ping(2) 查找指定的文件find 搜索的目录名 -name '被搜索的文件名'例子: find / -name 'a.*' 在根目录下搜索a.开头的文件。
android 应用安装过程,Android应用程序安装过程浅析(3)
weixin_35738834的博客
05-26 413
本文来自网易云社区作者:孙有军这里post一个Runnable来执行内部的逻辑,主要做了如下操作: 1,锁定后安装包,通过调用installPackageLI来进行的 2,接下来都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过发送what为POST_INSTALL的message来继续处理 我们先来看看installPack...
[转]C# 应用程序安装部署步骤,安装前操作,先退出程序后卸载。
weixin_30508309的博客
09-11 263
1、点击【文件】-【建】-【项目】-其他项目类型-安装和部署,选择安装项目,在下面的名称栏填写SetupTest(或者选择安装向导,一直点击【下一步】)2、安装项目----六个子项依次为:文件系统编辑器、注册表编辑器、文件类型编辑器、用户界面编辑器、自定义操作编辑器、启动条件编辑器。其中我们在打包过程中经常要用到的有文件系统编辑器、注册表编辑器、用户界面编辑器和自定义操作编辑器。3...
Android中APK的安装流程
mockingbirds的专栏
11-07 8436
这篇文章,来学习apk的安装流程,在开始之前,先看看我们在APP中是通过下面代码来安装apk的Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.parse("file://" + path),"applica
Android-Apk的安装流程分析(一)
诺~~~
01-11 4433
Android-app的安装流程分析
端午送祝福语小程序源码(可对接流量主)
最新发布
05-26
该小程序的作用就是祝福语生成距离端午节也不远了,可以抓住机会蹭一波流量用户可以点击直接发送祝福语给好友 分享的时候会显示用。
应用程序安装失败-15
06-06
应用程序安装失败-15通常是由于Google Play Store应用程序的缓存或数据出现问题引起的。尝试以下步骤来解决此问题: 1. 清除Google Play Store应用程序的缓存和数据。您可以在“设置”>“应用程序”>“Google Play Store”中找到此选项。 2. 禁用并重启用Google Play Store应用程序。您可以在“设置”>“应用程序”>“Google Play Store”中找到此选项。 3. 确保您的设备已连接到稳定的Wi-Fi网络或数据网络。 4. 确保您的设备已更到最的操作系统版本。 如果上述方法仍无法解决问题,则可能需要联系设备制造商或Google支持团队以获取更多帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现 21984
  • Android OTA在线升级一(架构分析) 21896
  • android 关于Dialog 圆角 18479
  • 单工通信、半双工通信和全双工通信 15367
  • 关于Android在线支付Alipay(支付宝)开发的经验分享 14554

分类专栏

  • cts
  • android adb 16篇
  • android app 228篇
  • android blog 50篇
  • android bugs 7篇
  • android system 200篇
  • android mms 35篇
  • android telephony 2篇
  • android 编译 32篇
  • android 题目 8篇
  • android root 8篇
  • android PMS+permission 24篇
  • android ota 13篇
  • android log 9篇
  • android boot issue 5篇
  • android launcher 2篇
  • android jni 8篇
  • android 安全 17篇
  • android input 9篇
  • android IPC 8篇
  • android power 7篇
  • android AMS 17篇
  • Linux 35篇
  • Linux shell 21篇
  • C/C++ 39篇
  • Java 48篇
  • sql 9篇
  • 故事 44篇
  • git 2篇
  • 计算机 17篇
  • UML、设计模式 34篇
  • html/js 11篇
  • acm 1篇
  • other 3篇
  • json 1篇
  • Linux driver 3篇
  • andro

最新评论

  • Android 签名机制 v1、v2、v3

    VaeZyc: 非常nice

  • Android 外置 SD 卡写入权限问题

    雪飘碧鸳: 为啥每次关机重新开机后,需要重新授权才能读写SD卡? 有没有授权后,即使关机再开机也不用再重复授权吗

  • Android 外置 SD 卡写入权限问题

    jambestwick: good

  • Android 外置 SD 卡写入权限问题

    jambestwick: good

  • 如何分析编译到某个阶段卡死的问题

    kwchangseu: 您好,请问多线程为什么会增加内存消耗?

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • Android 操作系统架构开篇, 写的很好推荐看看
  • global reference table overflow
  • 浅谈Google认证失败项分析
2021年6篇
2020年11篇
2019年4篇
2018年15篇
2017年4篇
2016年39篇
2015年42篇
2014年79篇
2013年638篇
2012年73篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳SEO优化公司阜阳如何制作网站哪家好巴中设计网站报价曲靖阿里店铺运营价格罗湖网站搭建多少钱坪地网络广告推广公司横岗英文网站建设公司海南网络营销推荐双龙百度竞价包年推广多少钱潍坊百搜标王报价晋城网站制作哪家好荷坳网络推广哪家好鄂州百度竞价包年推广公司西宁建设网站平湖百姓网标王哪家好黄石网站优化推广公司定西关键词按天计费赣州百度seo公司哈密网页设计哪家好益阳网站改版多少钱抚顺网站改版公司临沂seo排名多少钱济源模板网站建设吉安网站建设推荐榆林网站搜索优化价格临沧外贸网站设计推荐济宁网站优化推广价格松岗网络广告推广哪家好东莞网站设计丽江优化报价重庆百度网站优化哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化