代码路径
注:
Android 7.1源码
frameworks/base/core/java/android/app/backup
frameworks/base/core/java/com/android/internal/backup
frameworks/base/services/backup
启动
com.android.server.SystemServer
private static final String BACKUP_MANAGER_SERVICE_CLASS =
"com.android.server.backup.BackupManagerService$Lifecycle";
private void startOtherServices() {
···
if (!disableNonCoreServices) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {//这个是软功能,通过system/etc下面的xml文件控制
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
····
}
···
}
com.android.server.backup.BackupManagerService
static Trampoline sInstance;
static Trampoline getInstance() {
// Always constructed during system bringup, so no need to lazy-init
return sInstance;
}
public static final class Lifecycle extends SystemService {
public Lifecycle(Context context) {
super(context);
sInstance = new Trampoline(context);
}
@Override
public void onStart() {
publishBinderService(Context.BACKUP_SERVICE, sInstance);//将BACKUP_SERVICE注册到ServiceManager,注意sInstance是Trampoline对象
}
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
sInstance.initialize(userId);//初始化状态,这里涉及BackupManagerService的初始化
····
}
}
···
}
com.android.server.backup.Trampoline
1)Trampoline extends IBackupManager.Stub
2)
volatile BackupManagerService mService;
public Trampoline(Context context) {
mContext = context;
File dir = new File(Environment.getDataDirectory(), "backup");
dir.mkdirs();
mSuppressFile = new File(dir, BACKUP_SUPPRESS_FILENAME);
mGlobalDisable = SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
}
// internal control API
public void initialize(final int whichUser) {
// Note that only the owner user is currently involved in backup/restore
// TODO: http://b/22388012
if (whichUser == UserHandle.USER_SYSTEM) {
// Does this product support backup/restore at all?
if (mGlobalDisable) {//通过此值可以判断是否初始化成功
Slog.i(TAG, "Backup/restore not supported");
return;
}
synchronized (this) {
if (!mSuppressFile.exists()) {//mSuppressFile 也会抑制mService初始化
mService = new BackupManagerService(mContext, this);
} else {
Slog.i(TAG, "Backup inactive in user " + whichUser);
}
}
}
}
小结
控制BackupManagerService启动的因素有几种:
a.config.disable_noncore 是否正常 默认false
b.android.software.backup 是否在/system/etc/**.xml定义
c.a和b正常,则可通过命令检查:
adb shell dumpsys backup 是否有Inactive
d.如果是不激活状态,可查具体原因:
a)adb shell getprop ro.backup.disable true代表不激活服务
b)/data/backup/backup-suppress 文件存在代表不激活服务
接口分析 fullBackup -- 全量备份接口
案例
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
ParcelFileDescriptor pf = ParcelFileDescriptor.open(mAppBackupFile, ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
bm.fullBackup(pf, true, false, false, false, false, true, false, new String[]{mCurrentUpgradeItem.getName()});
}
问题:接口具体备份了哪些文件?怎么实现的?
a)简介
/**
* Write a full backup of the given package to the supplied file descriptor.
* The fd may be a socket or other non-seekable destination. If no package names
* are supplied, then every application on the device will be backed up to the output.
*
* This method is synchronous -- it does not return until the backup has
* completed.
*
*
Callers must hold the android.permission.BACKUP permission to use this method.
*
* @param fd The file descriptor to which a 'tar' file stream is to be written
* @param includeApks If true
, the resulting tar stream will include the
* application .apk files themselves as well as their data.
* @param includeObbs If true
, the resulting tar stream will include any
* application expansion (OBB) files themselves belonging to each application.
* @param includeShared If true
, the resulting tar stream will include
* the contents of the device's shared storage (SD card or equivalent).
* @param allApps If true
, the resulting tar stream will include all
* installed applications' data, not just those named in the packageNames
* parameter.
* @param allIncludesSystem If {@code true}, then {@code allApps} will be interpreted
* as including packages pre-installed as part of the system. If {@code false},
* then setting {@code allApps} to {@code true} will mean only that all 3rd-party
* applications will be included in the dataset.
* @param packageNames The package names of the apps whose data (and optionally .apk files)
* are to be backed up. The allApps
parameter supersedes this.
*/
void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
boolean doCompress, in String[] packageNames);
b)具体流程
1)Trampoline.fullBackup -- > BackupManagerService.fullBackup
public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
boolean includeObbs, boolean includeShared, boolean doWidgets,
boolean doAllApps, boolean includeSystem, boolean compress, String[] pkgList) {
······
try {
······
FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
includeShared, doWidgets, doAllApps, includeSystem, compress, pkgList);
final int token = generateToken();
synchronized (mFullConfirmations) {
mFullConfirmations.put(token, params);//高级用法,将自定义变量放在本地,只是把标签跨进程传出去
}
······
// start up the confirmation UI
if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {//增加用户体验:调用UI进程让用户自行选择是否需要备份
mFullConfirmations.delete(token);
return;
}
·······
// start the confirmation countdown
startConfirmationTimeout(token, params);//监听UI进程的反馈
// wait for the backup to be performed
waitForCompletion(params);//等待
} finally {
······
}
}
boolean startConfirmationUi(int token, String action) {//注意:这里可以定制化
try {
Intent confIntent = new Intent(action);
confIntent.setClassName("com.android.backupconfirm",
"com.android.backupconfirm.BackupRestoreConfirmation");
confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);//注意只传了一个token
confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
} catch (ActivityNotFoundException e) {
return false;
}
return true;
}
关注进程间通信:
A-->System_server
System_server-->B
B-->System_server
此过程在没有回调方法的前提下,怎么实现数据对接的?又是怎么监听跨进程的任务完成?
2)BackupRestoreConfirmation.sendAcknowledgement
代码路径:
frameworks/base/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
public class BackupRestoreConfirmation extends Activity {
@Override
public void onCreate(Bundle icicle) {
······
mAllowButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendAcknowledgement(mToken, true, mObserver);
······
}
});
······
}
void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
try {
mBackupManager.acknowledgeFullBackupOrRestore(mToken,
allow,
String.valueOf(mCurPassword.getText()),
String.valueOf(encPassword),
mObserver);//UI进程调进system_server进程,关注mToken
} catch (RemoteException e) {
}
}
}
3)Trampoline.acknowledgeFullBackupOrRestore -- > BackupManagerService.acknowledgeFullBackupOrRestore
public void acknowledgeFullBackupOrRestore(int token, boolean allow,
String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
······
try {
FullParams params;
synchronized (mFullConfirmations) {
params = mFullConfirmations.get(token);//通过token获取保存的对象
if (params != null) {
mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);//移除超时监听
mFullConfirmations.delete(token);//从队列中删除保存对象
if (allow) {
final int verb = params instanceof FullBackupParams
? MSG_RUN_ADB_BACKUP//因为params为FullBackupParams,所以是MSG_RUN_ADB_BACKUP
: MSG_RUN_ADB_RESTORE;
params.observer = observer;//跨进程的回调对象
······
Message msg = mBackupHandler.obtainMessage(verb, params);
mBackupHandler.sendMessage(msg);
} else {
······
}
} else {
Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
}
}
} finally {
······
}
}
4)BackupManagerService.BackupHandler(what=MSG_RUN_ADB_BACKUP)
BackupHandler
public void handleMessage(Message msg)
case MSG_RUN_ADB_BACKUP:
{
FullBackupParams params = (FullBackupParams)msg.obj;
PerformAdbBackupTask task = new PerformAdbBackupTask(params.fd,
params.observer, params.includeApks, params.includeObbs,
params.includeShared, params.doWidgets,
params.curPassword, params.encryptPassword,
params.allApps, params.includeSystem, params.doCompress,
params.packages, params.latch);
(new Thread(task, "adb-backup")).start();//执行线程,关注task
break;
}
5)BackupManagerService.PerformAdbBackupTask.run
PerformAdbBackupTask
public void run() {
······
sendStartBackup();//通知回调方法onStartBackup
······
if (mPackages != null) {
addPackagesToSet(packagesToBackup, mPackages);//packagesToBackup初始化需要备份的app
}
······
ArrayList backupQueue =
new ArrayList(packagesToBackup.values());//需要备份的队列
FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());//mOutputFile就是fullBackup传入的fd
OutputStream out = null;
······
try {
······
OutputStream finalOutput = ofstream;
······
try {
······
out = finalOutput;
} catch (Exception e) {
······
}
······
int N = backupQueue.size();
for (int i = 0; i < N; i++) {
pkg = backupQueue.get(i);
final boolean isSharedStorage =
pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
mBackupEngine = new FullBackupEngine(out, null, pkg, mIncludeApks, this);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);//通知回调方法onBackupPackage
mBackupEngine.backupOnePackage();//执行app的备份工作
······
}
} catch (RemoteException e) {
} catch (Exception e) {
} finally {
······
synchronized (mLatch) {
mLatch.set(true);
mLatch.notifyAll();//这里就是解除fullBackup的等待
}
sendEndBackup();//通知回调方法onEndBackup
·······
}
}
5)BackupManagerService.FullBackupEngine.backupOnePackage
FullBackupEngine
public int backupOnePackage() throws RemoteException {
int result = BackupTransport.AGENT_ERROR;
if (initializeAgent()) {//初始化需要备份的app的mAgent。后面专题特殊说明
ParcelFileDescriptor[] pipes = null;
try {
pipes = ParcelFileDescriptor.createPipe();//创建管道,[0]读 [1]写
······
final int token = generateToken();
FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
token, sendApk, !isSharedStorage, widgetBlob);
pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
Thread t = new Thread(runner, "app-data-runner");
t.start();//将数据写入管道[1]中,这也是一种跨进程的通信。解决跨进程间获取资源文件问题
routeSocketDataToOutput(pipes[0], mOutput);//从管道[0]中读出来的数据,写入我们fullBackup制定的fd中
······
} catch (IOException e) {
} finally {
}
} else {
Slog.w(TAG, "Unable to bind to full agent for " + mPkg.packageName);
}
······
return result;
}
高级用法:
采用匿名管道实现跨进程获取资源文件
6)BackupManagerService.FullBackupEngine.FullBackupRunner.run
FullBackupRunner
public void run() {
try {
FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
·······
if (mSendApk) {
writeApkToBackup(mPackage, output);//备份apk
}
······
mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);//备份apk的用户数据
} catch (IOException e) {
} catch (RemoteException e) {
} finally {
}
}
private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
······
final String appSourceDir = pkg.applicationInfo.getBaseCodePath();//路径,例如/data/app/com.test.test-1/base.apk
final String apkDir = new File(appSourceDir).getParent();
FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
apkDir, appSourceDir, output);//打包apk
······
}
注意:
1.output仅仅是管道[1]的载体,真正写到我们指定的文件是通过从管道[0]中读出数据,填充到指定fd
2.备份的apk文件路径为:
/data/app/***/base.apk
3.调用android.app.backup.FullBackup.backupToTar方法
7)BackupManagerService.FullBackupEngine.mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder); 注: 此业务代码路线比较长,需要特别拿出来说
(a)先说重点,此方法打包的就是app运行的用户资源。例如:
/data/user/0/包名/*
(b)代码实现
(1)mAgent初始化:BackupManagerService.FullBackupEngine.initializeAgent
BackupManagerService.FullBackupEngine.backupOnePackage可知:
BackupManagerService.FullBackupEngine.initializeAgent
private boolean initializeAgent() {
if (mAgent == null) {
mAgent = bindToAgentSynchronous(mPkg.applicationInfo,
IApplicationThread.BACKUP_MODE_FULL);
}
return mAgent != null;
}
(2)BackupManagerService.bindToAgentSynchronous
IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
IBackupAgent agent = null;
synchronized(mAgentConnectLock) {
mConnecting = true;
mConnectedAgent = null;
try {
if (mActivityManager.bindBackupAgent(app.packageName, mode,
UserHandle.USER_OWNER)) {//通过Ams绑定app获取mConnectedAgent
long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
while (mConnecting && mConnectedAgent == null
&& (System.currentTimeMillis() < timeoutMark)) {//等待mConnectedAgent被赋值
try {
mAgentConnectLock.wait(5000);//休眠等待被通知
} catch (InterruptedException e) {
// just bail
Slog.w(TAG, "Interrupted: " + e);
mConnecting = false;
mConnectedAgent = null;
}
}
······
agent = mConnectedAgent;//期待在等待的时间内,mConnectedAgent被赋予成功
}
} catch (RemoteException e) {
}
}
if (agent == null) {
try {
mActivityManager.clearPendingBackup();
} catch (RemoteException e) {
// can't happen - ActivityManager is local
}
}
return agent;
}
(3)ActivityManagerService.bindBackupAgent
public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
······
synchronized(this) {
······
ProcessRecord proc = startProcessLocked(app.processName, app,
false, 0, "backup", hostingName, false, false, false);//启动app
if (proc == null) {
Slog.e(TAG, "Unable to start backup agent process " + r);
return false;
}
······
if (proc.thread != null) {
······
try {
proc.thread.scheduleCreateBackupAgent(app,
compatibilityInfoForPackageLocked(app), backupMode);//执行IApplicationThread,scheduleCreateBackupAgent
} catch (RemoteException e) {
// Will time out on the backup manager side
}
} else {
}
······
}
return true;
}
(4)ApplicationThreadNative.java ApplicationThreadProxy.scheduleCreateBackupAgent
代码路径:
frameworks/base/core/java/android/app/ApplicatinThreadNative
ApplicationThreadProxy
public final void scheduleCreateBackupAgent(ApplicationInfo app,
CompatibilityInfo compatInfo, int backupMode) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
app.writeToParcel(data, 0);
compatInfo.writeToParcel(data, 0);
data.writeInt(backupMode);
mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
ApplicationThreadNative
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
······
case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
int backupMode = data.readInt();
scheduleCreateBackupAgent(appInfo, compatInfo, backupMode);//执行ActivityThread.scheduleCreateBackupAgent
return true;
}
}
(5)ActivityThread.scheduleCreateBackupAgent
代码路径:
frameworks/base/core/java/android/app/ActivityThread
public final void scheduleCreateBackupAgent(ApplicationInfo app,
CompatibilityInfo compatInfo, int backupMode) {
CreateBackupAgentData d = new CreateBackupAgentData();
d.appInfo = app;
d.compatInfo = compatInfo;
d.backupMode = backupMode;
sendMessage(H.CREATE_BACKUP_AGENT, d);
}
ActivityThread.H.handleMessage(what=CREATE_BACKUP_AGENT) --> ActivityThread.handleCreateBackupAgent
private void handleCreateBackupAgent(CreateBackupAgentData data) {
······
String classname = data.appInfo.backupAgentName;
// full backup operation but no app-supplied agent? use the default implementation
if (classname == null && (data.backupMode == IApplicationThread.BACKUP_MODE_FULL
|| data.backupMode == IApplicationThread.BACKUP_MODE_RESTORE_FULL)) {
classname = "android.app.backup.FullBackupAgent";//默认Agent类
}
try {
IBinder binder = null;
BackupAgent agent = mBackupAgents.get(packageName);
if (agent != null) {
······
binder = agent.onBind();
} else {
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
agent = (BackupAgent) cl.loadClass(classname).newInstance();
// set up the agent's context
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(agent);
agent.attach(context);
agent.onCreate();
binder = agent.onBind();//获取agent的binder对象
mBackupAgents.put(packageName, agent);
} catch (Exception e) {
}
}
// tell the OS that we're live now
try {
ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder);//调用AMS.backupAgentCreated
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
throw new RuntimeException("Unable to create BackupAgent "
+ classname + ": " + e.toString(), e);
}
}
跨进程调入app内部,再从app内部跨进程告知Ams,并带上binder对象
(6)ActivityManagerService.backupAgentCreated
public void backupAgentCreated(String agentPackageName, IBinder agent) {
······
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentConnected(agentPackageName, agent);//调进BackupManagerService.agentConnected
} catch (RemoteException e) {
} catch (Exception e) {
} finally {
}
}
(7)Trampoline.agentConnected-->BackupManagerService.agentConnected
public void agentConnected(String packageName, IBinder agentBinder) {
synchronized(mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
mConnectedAgent = agent;//终于见到本尊的初始化了
mConnecting = false;
} else {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ " claiming agent connected");
}
mAgentConnectLock.notifyAll();//唤醒对应的线程,可见(2)
}
}
至此,mConnectedAgent的对象已知,为BackupAgent
(c)BackupAgent.BackupServiceBinder.doFullBackup
BackupAgent.BackupServiceBinder.doFullBackup
public void doFullBackup(ParcelFileDescriptor data,
int token, IBackupManager callbackBinder) {
······
try {
BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
} catch (IOException ex) {
} catch (RuntimeException ex) {
} finally {
}
}
BackupAgent.onFullBackup
public void onFullBackup(FullBackupDataOutput data) throws IOException {
//这里涉及app的用户数据备份,例如:sp、db
//最关键的applyXmlFiltersAndDoFullBackupForDomain
//fullBackupFileTree-->FullBackup.backupToTar
}
8)重点解读:FullBackup.backupToTar
代码路径:
frameworks/base/core/java/android/app/backup/FullBackup.java
frameworks/base/core/jni/android_app_backup_FullBackup.cpp
frameworks/base/libs/androidfw/BackupHelpers.cpp
FullBackup.java
static public native int backupToTar(String packageName, String domain,
String linkdomain, String rootpath, String path, FullBackupDataOutput output);
android_app_backup_FullBackup.cpp
static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
······
jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
if (!err) {
//ALOGI("measured [%s] at %lld", path.string(), (long long) tarSize);
env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
}
return err;
}
BackupHelpers.cpp
int write_tarfile(const String8& packageName, const String8& domain,
const String8& rootpath, const String8& filepath, off_t* outSize,
BackupDataWriter* writer)
{
//可以自行查看代码
········
}
c)简化流程
1)备份业务:
BackupManagerService.fullBackup
|
|
|/
BackupRestoreConfirmation
(调用BackupManagerService.acknowledgeFullBackupOrRestore区分点FULL_BACKUP_INTENT_ACTION)
|
|
|/
BackupManagerService
MSG_RUN_ADB_BACKUP (BackupHandler)
PerformAdbBackupTask (FullBackupParams)
run 调用如下1-BackupManagerService
|
|
|/
1-BackupManagerService
FullBackupEngine
FullBackupRunner
writeApkToBackup//备份app
mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);//备份app涉及的数据
关键点:
FullBackup.backupToTar
2)恢复业务:
BackupManagerService.fullRestore
|
|
|/
BackupRestoreConfirmation
(调用BackupManagerService.acknowledgeFullBackupOrRestore区分点FULL_RESTORE_INTENT_ACTION)
|
|
|/
BackupManagerService
MSG_RUN_ADB_RESTORE (BackupHandler)
PerformAdbRestoreTask (FullRestoreParams)
run
restoreOneFile
installApk
3)访问的权限要求
android.permission.BACKUP --- signature|privileged
亮点解读
1.怎么实现跨进程等待?
案例1): 调用接口fullBackup,需要等待用户确认
等待过程:
public final AtomicBoolean latch;
void waitForCompletion(FullParams params) {
synchronized (params.latch) {
while (params.latch.get() == false) {
try {
params.latch.wait();
} catch (InterruptedException e) { /* never interrupted */ }
}
}
}
确认过程(确认后,等待的wait自动退出)
方案1)
void signalFullBackupRestoreCompletion(FullParams params) {
synchronized (params.latch) {
params.latch.set(true);
params.latch.notifyAll();
}
}
方案2)
final AtomicBoolean mLatch;
synchronized (mLatch) {
mLatch.set(true);
mLatch.notifyAll();
}
总结:
对象的wait和notifyAll方法使用
案例2):
备份app数据需要启动Agent,等待确认
等待过程:
IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
synchronized(mAgentConnectLock) {
try {
if (mActivityManager.bindBackupAgent(app.packageName, mode,
UserHandle.USER_OWNER)) {
long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
while (mConnecting && mConnectedAgent == null
&& (System.currentTimeMillis() < timeoutMark)) {
try {
mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
}
}
}
} catch (RemoteException e) {
// can't happen - ActivityManager is local
}
}
}
确认过程
public void agentConnected(String packageName, IBinder agentBinder) {
synchronized(mAgentConnectLock) {
·····
mAgentConnectLock.notifyAll();
}
}
总结
对象的wait和notifyAll方法使用
2.app的用户数据怎么被打包到指定文件?
采用管道,因为父子进程(fork)共享内存拷贝
ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe();
[0] 读
[1] 写
参考学习:
https://www.jianshu.com/p/c2a8987e1c0d
https://www.jianshu.com/p/115cf0e519c2
问题记录:
aAsset path /data/cache/backup_stage/**.apk is neither a directory nor file (type=0).
参考学习
https://www.52pojie.cn/forum.php?mod=viewthread&tid=1060641
https://github.com/IzZzI/BackUpDemo
https://bbs.125.la/thread-14425656-1-1.html?goto=lastpost
https://blog.csdn.net/self_study/article/details/58587412
https://blog.csdn.net/u013334392/article/details/81392097
https://www.jianshu.com/p/c2a8987e1c0d
https://www.jianshu.com/p/115cf0e519c2
http://www.bubuko.com/infodetail-1890598.html
https://www.cnblogs.com/lipeineng/p/6237681.html