如何把手机上的作文安装在手机安装包解析错误中

现在的位置:
>, >正文
怎样使用Xperia Flasher(Flashtool)工具创建一个FTF固件安装包文件?
作为一名Sony Xperia用户,我们猜想大多数人们都知道官方固件,那是一种简单易行的升级方法。而如果你更深入一点的话,或者你在网上冲浪时有所留意的话,你应当发现了些什么,就拿我们自己来说,我们在写Sony官方固件更新的相关文章时,基本都附了可以通过手动安装的FTF固件本地安装包文件。我们想说的是,你有没有想过这样的FTF文件是从哪里来的呢?没错,当然是互联网,我们也是通过Google获取。但是,有一点,最起初放到互联网的人们又是怎么获取的呢?源自Sony官方?非也,它基本都是第三方开发者、Sony爱好者们或Sony用户们通过Sony官方的固件文件转译过来的。
而就如何转制的问题,源自第三方开发社区的Xperia Flash Tool(或叫做Xperia Flasher)便是一个很好的工具。不得不惊呼Flash Tool的强大吧,不仅仅可以写入FTF,还可以编译或制作它。
因此,为了让大家明白如何使用Xperia Flash Tool工具来制作一个FTF Sony官方固件的本地安装包文件,我们写下这篇教程,它只面向Windows用户。
首先,你要知道一点,既然制作源是官方固件,它自然来自Sony,而这个源固件实际上就是通过或等获取的,因为你在通过Sony PC套件更新官方固件的同时,官方的固件包也已下载下来了,你甚至可以只通过它下载官方固件而不升级。
然后,你必须知道Sony PC套件下载下来的固件所在位置:
1、Sony PC Companion,
Windows XP,C:\Documents and Settings\All Users\Application Data\Sony Mobile\Update Engine\db\\blob_fs
Windows Vista、Windows 7与Windows 8,C:\ProgramData\Sony Mobile\Update Engine\db\\blob_fs
2、Sony Update Service,
32位Windows,C:\Program Files\Sony Mobile\Update Service\db\\blob_fs
64位Windows,C:\Program Files (x86)\Sony Mobile\Update Service\db\\blob_fs
这里,我们顺带提醒一下,如果你的Sony Xperia设备已,你可能没有办法通过Sony PC套件来更新或下载固件。这种情况下也不要担心,你可以使用Sony官方固件升级工具。
接下来,我们就开始将原封不动的官方原始固件文件转换到Xperia Flash Tool可写入的FTF格式。
使用Xperia Flash Tool制作FTF固件文件
运行Xperia Flash Tool
点Tools——SEUS Decrypt(工具——SEUS编译),选择源固件文件夹,然后将Available files(可用文件)框下的所有文件移动到Files to convert(文件转换)框下,点Convert(转换)
转换完成后就是输入并创建固件的详细信息了:
Device,设备型号,像Xperia Z的C6602、C6603等等,你可以在设备的Settings——About(设置——关于)菜单找到,它显示在Model number一栏
Branding,定制版本,即运营商版或通用版,如香港通用版即Generic HK等,拿Xperia Z来说吧,你可以看看一文中表格的第二列
Verison,固件构建,如10.3.A.0.423等等
这时将folder list(文件夹列表)框下除了.ta文件(如simlock.ta等)外的所有文件移动到Firmware content(固件内容)框下,点Create(创建)
当你看到信息窗口显示Bundle creation finished(文件包创建完成)时,你的FTF文件即已创建成功,它通常位于X:\Flashtool\firmwares目录,比如你的Flashtool工具安装在C盘,则为C:\Flashtool\firmwares
似乎经过了一个漫长的过程吧,但完成后的成就感还是挺美的吧。
实际上,你不仅能抽出FTF官方固件文件,你甚至还能呢!要不?去试试?
本文固定链接:
【上篇】【下篇】
您可能还会对这些文章感兴趣!为什么我的世界手机中文版安装不了,以前都安装得了的。_百度知道
为什么我的世界手机中文版安装不了,以前都安装得了的。
可能是手机配置问题或者是你设置了什么权限
应该没有吧!
我刚刚安装了,结果发现那个不是中文版的,之后又删了,再次下载的时候就安装不了
那就应该是设置了
其他类似问题
为您推荐:
其他5条回答
首页小学作文初中作文高中作文
观察蜗牛的作文400字(2)
作者:我读初中 | 阅读:289 | 时间:
听说把蜗牛放进水里,就可以使它探出头来。恰巧,这只蜗牛被我吓得把头缩进去了,我打了一些水,往里面倒。不一会儿,蜗牛就乖乖地探出头来,并伸出两个长长的触角。我仔细地用放大镜观察它。发现,蜗牛的下腹部有很多点点,这是什么呢?有什么作用呢?我又继续观察,发现蜗牛的壳上还有一个小洞,这又是起什么作用的呢?于是,我带着这两个疑问来到妈妈面前询问。妈妈回答说:“蜗牛腹部下面的小点是它的腹足,蜗牛就是依靠这些腹足爬行的,壳上的洞是蜗牛的气孔呀!”“噢!”我恍然大悟。
爷爷告诉我,蜗牛通常是晚上出来活动。因为它害怕白天直射的阳光,喜欢栖息在阴暗潮湿的环境里,所以一般...
你发这个给我干嘛。
原先我也用5830,后来也是装不上,在老衲的多次查询中,得出了重要的结论
把安装包卸了,找个别的APP重下,比如应用宝
我先去下载看看。
有可能你下载的 这个版本与手机不兼容 所以不能安装
内存不够了
我的内存是够的。
那就是下载的资源有问题。
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁android PackageInstaller那点事儿
今天简单讲解一下PackageInstaller
文件路径:
下面开始讲解:
首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。
当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上application/vnd.android.package-archive这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+/.../packageName.
File apkFile = new File(apkFileString);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(apkFile), application/vnd.android.package-archive);
mContext.startActivity(intent);
这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看
final Intent intent = getIntent();
mPackageURI = intent.getData();
mPm = getPackageManager();
mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);
获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了
public static
PackageParser.Package getPackageInfo(Uri packageURI) {
final String archiveFilePath = packageURI.getPath();
PackageParser packageParser = new PackageParser(archiveFilePath);
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
PackageParser.Package pkg =
packageParser.parsePackage(sourceFile,
archiveFilePath, metrics, 0);
// Nuke the parser reference.
packageParser =
生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了
public Package parsePackage(File sourceFile, String destCodePath,
DisplayMetrics metrics, int flags) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = sourceFile.getPath();
if (!sourceFile.isFile()) {
Slog.w(TAG, Skipping dir:
+ mArchiveSourcePath);
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
if (!isPackageFilename(sourceFile.getName())
&& (flags&PARSE_MUST_BE_APK) != 0) {
if ((flags&PARSE_IS_SYSTEM) == 0) {
// We expect to have non-.apk files in the system dir,
// so don't warn about them.
Slog.w(TAG, Skipping non-package file:
+ mArchiveSourcePath);
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
if (DEBUG_JAR)
Slog.d(TAG, Scanning package:
+ mArchiveSourcePath);
XmlResourceParser parser =
AssetManager assmgr =
Resources res =
boolean assetError =
assmgr = new AssetManager();
int cookie = assmgr.addAssetPath(mArchiveSourcePath);
if (cookie != 0) {
res = new Resources(assmgr, metrics, null);
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
assetError =
Slog.w(TAG, Failed adding asset path:+mArchiveSourcePath);
} catch (Exception e) {
Slog.w(TAG, Unable to read AndroidManifest.xml of
+ mArchiveSourcePath, e);
if (assetError) {
if (assmgr != null) assmgr.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
String[] errorText = new String[1];
Package pkg =
Exception errorException =
// XXXX todo: need to figure out correct configuration.
pkg = parsePackage(res, parser, flags, errorText);
} catch (Exception e) {
errorException =
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
if (pkg == null) {
// If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
// just means to skip this app so don't make a fuss about it.
if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
if (errorException != null) {
Slog.w(TAG, mArchiveSourcePath, errorException);
Slog.w(TAG, mArchiveSourcePath +
+ parser.getPositionDescription()
+ errorText[0]);
if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
parser.close();
assmgr.close();
parser.close();
assmgr.close();
// Set code and resource paths
pkg.mPath = destCodeP
pkg.mScanPath = mArchiveSourceP
//pkg.applicationInfo.sourceDir = destCodeP
//pkg.applicationInfo.publicSourceDir = destR
pkg.mSignatures =
首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是
XmlResourceParser parser =
AssetManager assmgr =
Resources res =
这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
assmgr = new AssetManager();
int cookie = assmgr.addAssetPath(mArchiveSourcePath);
通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源
res = new Resources(assmgr, metrics, null);
当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
ANDROID_MANIFEST_FILENAME也就是
private static final String ANDROID_MANIFEST_FILENAME = AndroidManifest.
这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
String[] errorText = new String[1];
Package pkg =
Exception errorException =
// XXXX todo: need to figure out correct configuration.
pkg = parsePackage(res, parser, flags, errorText);
} catch (Exception e) {
errorException =
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的
String pkgName = parsePackageName(parser, attrs, flags, outError);
获取包名。
final Package pkg = new Package(pkgName);
boolean foundApp =
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() & 0) {
String nameError = validateName(str, true);
if (nameError != null && !android.equals(pkgName)) {
outError[0] =
specifies bad sharedUserId name
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
sa.recycle();
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installL
解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如application也就是
这里面包含的信息,例如这里的lable等等,还有以其为父的activity,receiver,service,provider等等,这里以activity为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
这里有很多变量,在定义一个acitivity的时候有的我们用过,有的没有用过,Xml文件资源解析器就是从xml中获取到这先变量的值然后付给这些变量;同样还有permission权限,也就是
这类,大家经常会见到的;还有permission-group,uses-sdk等等吧,有兴趣的可以一一研究。最终这些信息都会囊括到我们的Package中。这里我们明白Package是什么了吧?就是包含包中所有信息的的玩意。到此位置我们的Package已经生成,然后我们还回到PackageInstallerActivity的onCreate中,接着往下看,不重要的就跳过了
initiateInstall()
private void initiateInstall() {
String pkgName = mPkgInfo.packageN
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length & 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.setPackageName(pkgName);
// Check if package is already installed. display confirmation dialog if replacing pkg
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
mAppInfo =
if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {
startInstallConfirm();
if(localLOGV) Log.i(TAG, Replacing existing package:+
mPkgInfo.applicationInfo.packageName);
showDialogInner(DLG_REPLACE_APP);
然后是这里的startInstallConfirm(),也就是
private void startInstallConfirm() {
LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);
LinearLayout securityList = (LinearLayout) permsSection.findViewById(
R.id.security_settings_list);
boolean permVisible =
if(mPkgInfo != null) {
AppSecurityPermissions
= new AppSecurityPermissions(this, mPkgInfo);
if(asp.getPermissionCount() & 0) {
permVisible =
securityList.addView(asp.getPermissionsView());
if(!permVisible){
permsSection.setVisibility(View.INVISIBLE);
mInstallConfirm.setVisibility(View.VISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
到这里我们的PackageInstallerActivity这个activity才算完成,这里我们看看我们安装界面的权限View是如何生成的,也就是asp.getPermissionsView(),这里的AppSecurityPermissions(this, mPkgInfo)传进去两个参数,一个是Context,一个是我们刚才获取到的Package,我们进去看看,文件在frameworks/base/core/java/android/widget下面
public AppSecurityPermissions(Context context, PackageParser.Package pkg) {
mContext =
mPm = mContext.getPackageManager();
mPermsList = new ArrayList();
Set permSet = new HashSet();
if(pkg == null) {
// Get requested permissions
if (pkg.requestedPermissions != null) {
ArrayList strList = pkg.requestedP
int size = strList.size();
if (size & 0) {
extractPerms(strList.toArray(new String[size]), permSet);
// Get permissions related to
shared user if any
if(pkg.mSharedUserId != null) {
int sharedU
sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);
getAllUsedPermissions(sharedUid, permSet);
} catch (NameNotFoundException e) {
Log.w(TAG, Could'nt retrieve shared user id for:+pkg.packageName);
// Retrieve list of permissions
for(PermissionInfo tmpInfo : permSet) {
mPermsList.add(tmpInfo);
就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是
public View getPermissionsView() {
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
mShowMore = mPermsView.findViewById(R.id.show_more);
mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);
mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);
mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);
mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);
mNoPermsView = mPermsView.findViewById(R.id.no_permissions);
// Set up the LinearLayout that acts like a list item.
mShowMore.setClickable(true);
mShowMore.setOnClickListener(this);
mShowMore.setFocusable(true);
// Pick up from framework resources instead.
mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);
mPermFormat = mContext.getString(R.string.permissions_format);
mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);
mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);
// Set permissions view
setPermissions(mPermsList);
return mPermsV
这里就是我们的权限View的布局了,也就是frameworks/base/core/res/res/layout下面的app_perms_summary.xml布局了,如果我们想修改权限VIew的话就要从这里开始了。我们去看看是如何生成的,也就是setPermissions(mPermsList)
private void setPermissions(List permList) {
mGroupLabelCache = new HashMap();
//add the default label so that uncategorized permissions can go here
mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);
// Map containing group names and a list of permissions under that group
// categorized as dangerous
mDangerousMap = new HashMap();
// Map containing group names and a list of permissions under that group
// categorized as normal
mNormalMap = new HashMap();
// Additional structures needed to ensure that permissions are unique under
// each group
Map& dangerousMap =
new HashMap&();
Map & normalMap =
new HashMap&();
PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);
if (permList != null) {
// First pass to group permissions
for (PermissionInfo pInfo : permList) {
if(localLOGV) Log.i(TAG, Processing permission:+pInfo.name);
if(!isDisplayablePermission(pInfo)) {
if(localLOGV) Log.i(TAG, Permission:+pInfo.name+ is not displayable);
Map & permInfoMap =
(pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?
dangerousMap : normalM
String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.
if(localLOGV) Log.i(TAG, Permission:+pInfo.name+ belongs to group:+grpName);
List grpPermsList = permInfoMap.get(grpName);
if(grpPermsList == null) {
grpPermsList = new ArrayList();
permInfoMap.put(grpName, grpPermsList);
grpPermsList.add(pInfo);
int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);
if(localLOGV) Log.i(TAG, idx=+idx+, list.size=+grpPermsList.size());
if (idx & 0) {
idx = -idx-1;
grpPermsList.add(idx, pInfo);
// Second pass to actually form the descriptions
// Look at dangerous permissions first
aggregateGroupDescs(dangerousMap, mDangerousMap);
aggregateGroupDescs(normalMap, mNormalMap);
mCurrentState = State.NO_PERMS;
if(mDangerousMap.size() & 0) {
mCurrentState = (mNormalMap.size() & 0) ? State.BOTH : State.DANGEROUS_ONLY;
} else if(mNormalMap.size() & 0) {
mCurrentState = State.NORMAL_ONLY;
if(localLOGV) Log.i(TAG, mCurrentState= + mCurrentState);
showPermissions();
这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()
private void showPermissions() {
switch(mCurrentState) {
case NO_PERMS:
displayNoPermissions();
case DANGEROUS_ONLY:
displayPermissions(true);
case NORMAL_ONLY:
displayPermissions(false);
case BOTH:
displayPermissions(true);
if (mExpanded) {
displayPermissions(false);
mShowMoreIcon.setImageDrawable(mShowMaxIcon);
mShowMoreText.setText(R.string.perms_hide);
mNonDangerousList.setVisibility(View.VISIBLE);
mShowMoreIcon.setImageDrawable(mShowMinIcon);
mShowMoreText.setText(R.string.perms_show_all);
mNonDangerousList.setVisibility(View.GONE);
mShowMore.setVisibility(View.VISIBLE);
给我们的布局赋显示的内容了,这里不一一解释,我们去看看displayPermissions
private void displayPermissions(boolean dangerous) {
Map permInfoMap = dangerous ? mDangerousMap : mNormalM
LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousL
permListView.removeAllViews();
Set permInfoStrSet = permInfoMap.keySet();
for (String loopPermGrpInfoStr : permInfoStrSet) {
CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);
//guaranteed that grpLabel wont be null since permissions without groups
//will belong to the default group
if(localLOGV) Log.i(TAG, Adding view group: + grpLabel + , desc:
+ permInfoMap.get(loopPermGrpInfoStr));
permListView.addView(getPermissionItemView(grpLabel,
permInfoMap.get(loopPermGrpInfoStr), dangerous));
看到这里就很明白了,我们的权限View是怎么生成的了。不再多做解释了。至此我们PackageInstallerActivity这个activity已经完全形成了,截个图吧
接下来,我们说说当点击&安装&之后做了什么事情。
public void onClick(View v) {
if(v == mOk) {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if(localLOGV) Log.i(TAG, downloaded app uri=+mPackageURI);
startActivity(newIntent);
} else if(v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
去启动了另外一个acitvity也就是InstallAppProgress,并过去几个数据,主要是mPkgInfo.applicationInfo也就是ApplicationInfo,还有mPackageURI也就是apkFile的Uri,还有一些其他的数据。然后我们就去InstallAppProgress的onCreate中
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = intent.getData();
initView();
获取到传过来的两个数据,然后就initView(),initView()里面是一些布局的初始化,不再赘述,只截取重要的,也就是
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
PackageInstallObserver observer = new PackageInstallObserver();
pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);
pm是PackageManager,这样我们就去PackageManager.installPackage中,在PackageManager的installPackage是abstract函数,具体实现在PackageManagerService中,这里传进去的参数第一个我们已经知道了,第二个是package安装的观察者
class PackageInstallObserver extends IPackageInstallObserver.Stub {
public void packageInstalled(String packageName, int returnCode) {
Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
Log.d(packageInstalled, returnCode = +returnCode);
msg.arg1 = returnC
mHandler.sendMessage(msg);
当安装完成就会走到packageInstalled个函数中,第三个参数是flag主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看
/* Called when a downloaded package installation has been confirmed by the user */
public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags) {
installPackage(packageURI, observer, flags, null);
/* Called when a downloaded package installation has been confirmed by the user */
public void installPackage(
final Uri packageURI, final IPackageInstallObserver observer, final int flags,
final String installerPackageName) {
installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,
也就是installPackageWithVerification
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,
int flags, String installerPackageName, Uri verificationURI,
ManifestDigest manifestDigest) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int uid = Binder.getCallingUid();
final int filteredF
if (uid == Process.SHELL_UID || uid == 0) {
if (DEBUG_INSTALL) {
Slog.v(TAG, Install from ADB);
filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;
filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
verificationURI, manifestDigest);
mHandler.sendMessage(msg);
就去发了一个消息INIT_COPY,并携带了我们传进来的参数组成的一个类InstallParams,InstallParams继承于HandlerParams,我们去看看这个消息执行了什么
case INIT_COPY: {
if (DEBUG_INSTALL) Slog.i(TAG, init_copy);
HandlerParams params = (HandlerParams) msg.
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, idx= + idx);
// If a bind was already initiated we dont really
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, Failed to bind to media container service);
params.serviceError();
// Once we bind to the service, the first
// pending request will be processed.
mPendingInstalls.add(idx, params);
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);
这里先 mPendingInstalls.add(idx, params)把我们要安装的信息放到HandlerParams的一个List中mPendingInstalls,然后去发了一个消息MCS_BOUND,也就是
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, mcs_bound);
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.
if (mContainerService == null) {
// Something seriously wrong. Bail out
Slog.e(TAG, Cannot bind to media container service);
for (HandlerParams params : mPendingInstalls) {
mPendingInstalls.remove(0);
// Indicate service bind error
params.serviceError();
mPendingInstalls.clear();
} else if (mPendingInstalls.size() & 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) {
// We are done...
look for more work or to
// go idle.
if (DEBUG_SD_INSTALL) Log.i(TAG,
Checking for more work or unbind...);
// Delete pending install
if (mPendingInstalls.size() & 0) {
mPendingInstalls.remove(0);
if (mPendingInstalls.size() == 0) {
if (mBound) {
if (DEBUG_SD_INSTALL) Log.i(TAG,
Posting delayed MCS_UNBIND);
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
if (DEBUG_SD_INSTALL) Log.i(TAG,
Posting MCS_BOUND for next woek);
mHandler.sendEmptyMessage(MCS_BOUND);
// Should never happen ideally.
Slog.w(TAG, Empty queue);
HandlerParams params = mPendingInstalls.get(0)读取出我们要安装的包信息,然后清楚该包信息,如果还有其他包就继续发MCS_BOUND这个消息,循环,直到都安装完了。然后安装在哪里呢?也就是
params.startCopy()
这个了,进去看看
final boolean startCopy() {
if (DEBUG_INSTALL) Slog.i(TAG, startCopy);
if (++mRetries & MAX_RETRIES) {
Slog.w(TAG, Failed to invoke remote methods on default container service. Giving up);
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
handleStartCopy();
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, Posting install MCS_RECONNECT);
mHandler.sendEmptyMessage(MCS_RECONNECT);
handleReturnCode();
这里的handleStartCopy()和handleServiceError()和handleReturnCode()都是abstract函数,希望大家还记得刚才我们发消息的时候携带的是InstallParams个类,InstallParams继承于HandlerParams,所以我们就会知道这三个abstract是在哪里实现的了,我们先说说handleStartCopy(),主要给两个变量完成了赋值工作也个是mArgs也就是InstallArgs,一个是ret标识是否安装成功的。handleServiceError()这个不讲解,然后看handleReturnCode()
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);
也就是processPendingInstall(mArgs, mRet)
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentS
res.uid = -1;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, true, res);
args.doPostInstall(res.returnCode);
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has a backupAgent defined.
final boolean update = res.removedInfo.removedPackage !=
boolean doRestore = (!update
&& res.pkg != null
&& res.pkg.applicationInfo.backupAgentName != null);
// Set up the post-install work request bookkeeping.
This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed.
Token values are &= 1.
if (mNextInstallToken & 0) mNextInstallToken = 1;
token = mNextInstallToken++;
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, + starting restore round-trip
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager.
It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, token
to BM for possible restore);
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} catch (RemoteException e) {
// can' the backup manager is local
} catch (Exception e) {
Slog.e(TAG, Exception trying to enqueue restore, e);
doRestore =
Slog.e(TAG, Backup Manager not found!);
doRestore =
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, No restore - queue post-install for
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
在这里启动了一个线程进行安装,也就是
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentS
res.uid = -1;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, true, res);
args.doPostInstall(res.returnCode);
也就是installPackageLI(args, true, res),这里代码较多,不再全部列出
if (replace) {
replacePackageLI(pkg, parseFlags, scanMode,
installerPackageName, res);
installNewPackageLI(pkg, parseFlags, scanMode,
installerPackageName,res);
很明显,如果是第一次安装走installNewPackageLI,如果是更新走replacePackageLI,我们去installNewPackageLI
* Install a non-existing package.
private void installNewPackageLI(PackageParser.Package pkg,
int parseFlags,
int scanMode,
String installerPackageName, PackageInstalledInfo res) {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageN
boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
res.name = pkgN
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.
Slog.w(TAG, Attempt to re-install
without first uninstalling package running as
+ mSettings.mRenamedPackages.get(pkgName));
res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {
// Don't allow installation over an existing package with the same name.
Slog.w(TAG, Attempt to re-install
without first uninstalling.);
res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
System.currentTimeMillis());
if (newPackage == null) {
Slog.w(TAG, Package couldn't be installed in
+ pkg.mPath);
if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {
res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;
updateSettingsLI(newPackage,
installerPackageName,
// delete the partially installed application. the data directory will have to be
// restored if it was already existing
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
// remove package from internal structures.
Note that we want deletePackageX to
// delete the package data and cache directories that it created in
// scanPackageLocked, unless those directories existed before we even tried to
// install.
deletePackageLI(
pkgName, false,
dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,
res.removedInfo, true);
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
System.currentTimeMillis());
其他都是判断一下到目前位置是否符合安装条件,也就是PackageManager.INSTALL_SUCCEEDED是否成功,如果成功就继续安装,不成功就重新安装或者返回了。scanPackageLI是一个重构函数,一个首参数是PackageParser.Package,一个首参数是File,我们看第一种,由于scanPackageLI是我们安装包的主要过程,有八百多行,做了很多安装需要的工作,具体在安装时做了什么工作,有兴趣的可以研究一下,这里就不再一一列出。我们只看
int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
mInstaller也就是Installer,所以去看看
public int install(String name, int uid, int gid) {
StringBuilder builder = new StringBuilder(install);
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
return execute(builder.toString());
execute也就是
private int execute(String cmd) {
String res = transaction(cmd);
return Integer.parseInt(res);
} catch (NumberFormatException ex) {
return -1;
transaction也就是
private synchronized String transaction(String cmd) {
if (!connect()) {
Slog.e(TAG, connection failed);
return -1;
if (!writeCommand(cmd)) {
* If installd died and restarted in the background (unlikely but
* possible) we'll fail on the next write (this one). Try to
* reconnect and write the command one more time before giving up.
Slog.e(TAG, write command failed? reconnect!);
if (!connect() || !writeCommand(cmd)) {
return -1;
if (LOCAL_DEBUG) {
Slog.i(TAG, send: ' + cmd + ');
if (readReply()) {
String s = new String(buf, 0, buflen);
if (LOCAL_DEBUG) {
Slog.i(TAG, recv: ' + s + ');
if (LOCAL_DEBUG) {
Slog.i(TAG, fail);
return -1;
writeCommand也就是
private boolean writeCommand(String _cmd) {
byte[] cmd = _cmd.getBytes();
int len = cmd.
if ((len & 1) || (len & 1024))
buf[0] = (byte) (len & 0xff);
buf[1] = (byte) ((len && 8) & 0xff);
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
} catch (IOException ex) {
Slog.e(TAG, write error);
disconnect();
mOut是什么呢?
private boolean connect() {
if (mSocket != null) {
Slog.i(TAG, connecting...);
mSocket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress(installd,
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
真实面目,原来这里在用Socket进行通信,把我们要安装的包信息告诉服务器,让服务器来完成余下的工作,这个服务器在底层,完成了一些copy等工作,具体是什么不再深究,如果以后有机会再讲解。
还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。

我要回帖

更多关于 手机安装包可以删除吗 的文章

 

随机推荐