1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.deviceinfo;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.net.TrafficStats;
24 import android.os.AsyncTask;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.os.UserHandle;
28 import android.os.storage.StorageManager;
29 import android.os.storage.VolumeInfo;
30 import android.telecom.Log;
31 import android.text.format.DateUtils;
32 import android.text.format.Formatter;
33 
34 import com.android.internal.app.IMediaContainerService;
35 
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.TimeUnit;
38 
39 import static com.android.settings.deviceinfo.StorageSettings.TAG;
40 
41 public abstract class MigrateEstimateTask extends AsyncTask<Void, Void, Long> implements
42         ServiceConnection {
43     private static final String EXTRA_SIZE_BYTES = "size_bytes";
44 
45     private static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
46             "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
47 
48     /**
49      * Assume roughly a Class 10 card.
50      */
51     private static final long SPEED_ESTIMATE_BPS = 10 * TrafficStats.MB_IN_BYTES;
52 
53     private final Context mContext;
54     private final StorageManager mStorage;
55 
56     private final CountDownLatch mConnected = new CountDownLatch(1);
57     private IMediaContainerService mService;
58 
59     private long mSizeBytes = -1;
60 
MigrateEstimateTask(Context context)61     public MigrateEstimateTask(Context context) {
62         mContext = context;
63         mStorage = context.getSystemService(StorageManager.class);
64     }
65 
copyFrom(Intent intent)66     public void copyFrom(Intent intent) {
67         mSizeBytes = intent.getLongExtra(EXTRA_SIZE_BYTES, -1);
68     }
69 
copyTo(Intent intent)70     public void copyTo(Intent intent) {
71         intent.putExtra(EXTRA_SIZE_BYTES, mSizeBytes);
72     }
73 
74     @Override
doInBackground(Void... params)75     protected Long doInBackground(Void... params) {
76         if (mSizeBytes != -1) {
77             return mSizeBytes;
78         }
79 
80         final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume();
81         final VolumeInfo emulatedVol = mStorage.findEmulatedForPrivate(privateVol);
82 
83         if (emulatedVol == null) {
84             Log.w(TAG, "Failed to find current primary storage");
85             return -1L;
86         }
87 
88         final String path = emulatedVol.getPath().getAbsolutePath();
89         Log.d(TAG, "Estimating for current path " + path);
90 
91         final Intent intent = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
92         mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
93 
94         try {
95             if (mConnected.await(15, TimeUnit.SECONDS)) {
96                 return mService.calculateDirectorySize(path);
97             }
98         } catch (InterruptedException | RemoteException e) {
99             Log.w(TAG, "Failed to measure " + path);
100         } finally {
101             mContext.unbindService(this);
102         }
103 
104         return -1L;
105     }
106 
107     @Override
onPostExecute(Long result)108     protected void onPostExecute(Long result) {
109         mSizeBytes = result;
110         long timeMillis = (mSizeBytes * DateUtils.SECOND_IN_MILLIS) / SPEED_ESTIMATE_BPS;
111         timeMillis = Math.max(timeMillis, DateUtils.SECOND_IN_MILLIS);
112 
113         final String size = Formatter.formatFileSize(mContext, mSizeBytes);
114         final String time = DateUtils.formatDuration(timeMillis).toString();
115         onPostExecute(size, time);
116     }
117 
onPostExecute(String size, String time)118     public abstract void onPostExecute(String size, String time);
119 
120     @Override
onServiceConnected(ComponentName name, IBinder service)121     public void onServiceConnected(ComponentName name, IBinder service) {
122         mService = IMediaContainerService.Stub.asInterface(service);
123         mConnected.countDown();
124     }
125 
126     @Override
onServiceDisconnected(ComponentName name)127     public void onServiceDisconnected(ComponentName name) {
128         // Ignored; we leave service in place for the background thread to
129         // run into DeadObjectException
130     }
131 }
132