本当のサイレントインストールは、ユーザーのクリックを必要とせず、ブログは言った、現在最もサイレントインストールまたはポップアップする必要は確認画面を、インストールを許可するユーザーを求めて、私はそれが本当のサイレント・インストールが操作を決定するために、任意のユーザーを必要としないされるべきだと思います、例えば、それがソフトウェア市場をインストールしたアプリケーションの多くは、今ある現実のサイレントインストールです。
同社はまた、キビや華為Googleは個人のアプリケーション市場があり、このアプリはアプリケーション市場交換を書き込まれないように、例えば、私は、アプリケーション市場のアプリがインストールされキビだろう、我々はそれを見つけ、同様の市場とアプリケーションのインストールを実現したいですHuawei社の携帯電話は、上記のキビの電話の内蔵におけるアプリケーションのようなサイレントインストールされるように達成することはできません。
通常の権限チェックには影響を与えないようにするために、我々は、ライン上のシステムのソースコードを修正インストールするための独自のインターフェースを追加する必要があります
次のように具体的な変更点は次のとおりです。
変更パッケージ/アプリケーション/ PackageInstaller / AndroidManifest.xmlを
次の権限を追加します。
<uses-permission android:name="android.permission.MANAGE_DOCUMENT" />
以下のレシーバを作成します。
<receiver android:name=".SilentInstallerReceiver" android:exported="true" >
<intent-filter>
<action android:name="xxx.intent.action.SILENT.INSTALLER" ></action>
<action android:name="xxx.intent.action.SILENT.INSTALLER.COMMIT"></action>
</intent-filter>
</receiver>
以下のドキュメントパッケージを追加/アプリケーション/ PackageInstaller / SRC / COM /アンドロイド/ packageinstaller / SilentInstallerReceiver.java
package com.android.packageinstaller;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.content.PackageHelper;
import com.android.packageinstaller.permission.utils.IoUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class SilentInstallerReceiver extends BroadcastReceiver {
private static final String TAG = "SilentInstallerReceiver";
private static final String BROADCAST_ACTION = "xxx.intent.action.SILENT.INSTALLER.COMMIT";
private static final String PACKAGENAME = "slient_name";
private static final String INSTLLPATH = "instll_path";
private static final String SCHEME_FILE = "file";
private static final String SCHEME_CONTENT = "content";
private static final String SCHEME_PACKAGE = "package";
private Context mContext;
private Uri mPackageURI;
private Intent mLaunchIntent;
private HandlerThread mInstallThread;
private Handler mInstallHandler;
private AsyncTask<Uri, Void, File> mStagingAsynTask;
private PackageManager mPm;
private PackageInfo mPkgInfo;
@Override
public void onReceive(Context context, Intent intent) {
mContext = context;
boolean wasSetUp = false;
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
mPm = context.getPackageManager();
String instllpath = intent.getStringExtra(INSTLLPATH);
// TODO this is lunch APP
if (!BROADCAST_ACTION.equals(intent.getAction())) {
if (TextUtils.isEmpty(instllpath)) {
return;
}
mPackageURI = Uri.parse(instllpath);
if (mPackageURI.getScheme() == null) {
instllpath = "file://" + instllpath;
mPackageURI = Uri.parse(instllpath);
}
Log.d(TAG, " mPackageURI = " + mPackageURI + " , intent getAction = " + intent.getAction());
wasSetUp = processPackageUri(mPackageURI);
}
if (BROADCAST_ACTION.equals(intent.getAction())) {
Log.d(TAG, " install app , so return !!! ");
onPackageInstalled(intent);
return;
}
if (mPackageURI == null) {
Log.d(TAG, " mPackageURI is null , so return !!! ");
return;
}
if (!wasSetUp) {
Log.d(TAG, " wasSetUp is false, return!!! ");
return;
}
initiateInstall();
}
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// 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.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
Log.d(TAG, " cdownloaded app uri= " + mPackageURI);
startSilentInstaller();
}
int getInstallFlags(String packageName) {
PackageManager pm = mContext.getPackageManager();
try {
PackageInfo pi =
pm.getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi != null) {
return PackageManager.INSTALL_REPLACE_EXISTING;
}
} catch (PackageManager.NameNotFoundException e) {
}
return 0;
}
void startSilentInstaller() {
final PackageManager pm = mContext.getPackageManager();
final int installFlags = getInstallFlags(mPkgInfo.applicationInfo.packageName);
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
Log.w(TAG, "Replacing package:" + mPkgInfo.applicationInfo.packageName);
}
Log.d(TAG, " starSilentInstaller + packageName " + mPkgInfo.applicationInfo.packageName);
final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
File file = new File(mPackageURI.getPath());
try {
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
params.setAppPackageName(pkg.packageName);
params.setInstallLocation(pkg.installLocation);
params.setSize(
PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
} catch (IOException e) {
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
}
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
try {
int c;
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// TODO this is lunch APP Broast
// Create a PendingIntent and use it to generate the IntentSender
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.putExtra(PACKAGENAME, mPkgInfo.applicationInfo.packageName);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
} finally {
IoUtils.closeQuietly(session);
}
}
/**
* TODO this is lunch APP Receiver Broast
* after app installed ,open the install app
*
* @param intent get insalled app name {@param BROADCAST_ACTION}
*/
void onPackageInstalled(Intent intent) {
Log.d(TAG, " install app is finishing, start luncher install app");
mLaunchIntent = mContext.getPackageManager().getLaunchIntentForPackage(
intent.getStringExtra(PACKAGENAME));
if (mLaunchIntent == null) return;
mLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(mLaunchIntent);
}
/**
* delete the apk
*/
private void clearCachedApkIfNeededAndFinish() {
if ("file".equals(mPackageURI.getScheme()) && mPackageURI.getPath() != null
&& mPackageURI.getPath().startsWith(mContext.getCacheDir().toString())) {
File file = new File(mPackageURI.getPath());
file.delete();
}
}
/**
* Parse the Uri and set up the installer for this package.
*
* @param packageUri The URI to parse
* @return {@code true} iff the installer could be set up
*/
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
Log.d(TAG, " scheme = " + scheme + " packageUri " + packageUri);
switch (scheme) {
case SCHEME_PACKAGE: {
try {
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + packageUri.getScheme()
+ " not available. Discontinuing installation");
return false;
}
}
break;
case SCHEME_FILE: {
File sourceFile = new File(packageUri.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
return false;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
}
break;
case SCHEME_CONTENT: {
mStagingAsynTask = new SilentInstallerReceiver.StagingAsyncTask();
mStagingAsynTask.execute(packageUri);
return false;
}
default: {
Log.w(TAG, "Unsupported scheme " + scheme);
clearCachedApkIfNeededAndFinish();
return false;
}
}
return true;
}
private final class StagingAsyncTask extends AsyncTask<Uri, Void, File> {
@Override
protected void onPreExecute() {
// TODO nothing to do
}
@Override
protected File doInBackground(Uri... params) {
if (params == null || params.length <= 0) {
return null;
}
Uri packageUri = params[0];
File sourceFile = null;
try {
sourceFile = File.createTempFile("package", ".apk", mContext.getApplicationContext().getCacheDir());
try (
InputStream in = mContext.getApplicationContext().getContentResolver().openInputStream(packageUri);
OutputStream out = (in != null) ? new FileOutputStream(
sourceFile) : null;
) {
// Despite the comments in ContentResolver#openInputStream
// the returned stream can be null.
if (in == null) {
return null;
}
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
// Be nice and respond to a cancellation
if (isCancelled()) {
return null;
}
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException ioe) {
Log.w(TAG, "Error staging apk from content URI", ioe);
if (sourceFile != null) {
sourceFile.delete();
}
}
return sourceFile;
}
@Override
protected void onPostExecute(File file) {
if (file == null) {
return;
}
Uri fileUri = Uri.fromFile(file);
boolean wasSetUp = processPackageUri(fileUri);
if (wasSetUp) {
initiateInstall();
}
}
@Override
protected void onCancelled(File file) {
// TODO nothing to do
}
}
}
作成した後は再コンパイルPackageInstaller APK電話内の電話を再起動するようにプッシュします。
ダウンロードしたファイルが良好な場合、次のようにサイレントインストールの検証作業を使用して、ADBブロードキャスト送信は、発行することができます。
adb shell am broadcast -a xxx.intent.action.SILENT.INSTALLER --es instllpath "apk路径"
徐は、時間と携帯電話のパフォーマンスの関係を待って、より良いパフォーマンス、短い待ち時間を、このコマンド待ち5-10秒を実行します。インストールが完了したら、処置は必要ありません、APKに直接プルアップ、
インストールが完了した後に引き上げることにしたくない場合は、以下のコードインジェクションをコメントアウト。
<action android:name="xxx.intent.action.SILENT.INSTALLER.COMMIT"></action>