1 /* 2 * Copyright (C) 2016 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.tv.settings.device.storage; 18 19 import android.annotation.Nullable; 20 import android.app.IntentService; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.storage.DiskInfo; 24 import android.os.storage.StorageManager; 25 import android.os.storage.VolumeInfo; 26 import android.support.v4.content.LocalBroadcastManager; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import java.util.List; 31 32 public class SettingsStorageService { 33 34 private static final String TAG = "SettingsStorageService"; 35 36 public static final String ACTION_FORMAT_AS_PUBLIC = 37 "com.android.tv.settings.device.storage.FORMAT_AS_PUBLIC"; 38 public static final String ACTION_FORMAT_AS_PRIVATE = 39 "com.android.tv.settings.device.storage.FORMAT_AS_PRIVATE"; 40 public static final String ACTION_UNMOUNT = "com.android.tv.settings.device.storage.UNMOUNT"; 41 42 public static final String EXTRA_SUCCESS = "com.android.tv.settings.device.storage.SUCCESS"; 43 public static final String EXTRA_INTERNAL_BENCH = 44 "com.android.tv.settings.device.storage.INTERNAL_BENCH"; 45 public static final String EXTRA_PRIVATE_BENCH = 46 "com.android.tv.settings.device.storage.PRIVATE_BENCH"; 47 48 public static void formatAsPublic(Context context, String diskId) { 49 final Intent intent = new Intent(context, Impl.class); 50 intent.setAction(ACTION_FORMAT_AS_PUBLIC); 51 intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 52 context.startService(intent); 53 } 54 55 public static void formatAsPrivate(Context context, String diskId) { 56 final Intent intent = new Intent(context, Impl.class); 57 intent.setAction(ACTION_FORMAT_AS_PRIVATE); 58 intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId); 59 context.startService(intent); 60 } 61 62 public static void unmount(Context context, String volumeId) { 63 final Intent intent = new Intent(context, Impl.class); 64 intent.setAction(ACTION_UNMOUNT); 65 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId); 66 context.startService(intent); 67 } 68 69 public static class Impl extends IntentService { 70 71 public Impl() { 72 super(Impl.class.getName()); 73 } 74 75 @Override 76 protected void onHandleIntent(@Nullable Intent intent) { 77 final String action = intent.getAction(); 78 if (TextUtils.isEmpty(action)) { 79 throw new IllegalArgumentException("Empty action in intent: " + intent); 80 } 81 82 switch (action) { 83 case ACTION_FORMAT_AS_PUBLIC: { 84 final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID); 85 if (TextUtils.isEmpty(diskId)) { 86 throw new IllegalArgumentException( 87 "No disk ID specified for format as public: " + intent); 88 } 89 formatAsPublic(diskId); 90 break; 91 } 92 case ACTION_FORMAT_AS_PRIVATE: { 93 final String diskId = intent.getStringExtra(DiskInfo.EXTRA_DISK_ID); 94 if (TextUtils.isEmpty(diskId)) { 95 throw new IllegalArgumentException( 96 "No disk ID specified for format as public: " + intent); 97 } 98 formatAsPrivate(diskId); 99 break; 100 } 101 case ACTION_UNMOUNT: { 102 final String volumeId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID); 103 if (TextUtils.isEmpty(volumeId)) { 104 throw new IllegalArgumentException("No volume ID specified for unmount: " 105 + intent); 106 } 107 unmount(volumeId); 108 break; 109 } 110 } 111 } 112 113 private void formatAsPublic(String diskId) { 114 try { 115 final StorageManager storageManager = getSystemService(StorageManager.class); 116 final List<VolumeInfo> volumes = storageManager.getVolumes(); 117 for (final VolumeInfo volume : volumes) { 118 if (TextUtils.equals(diskId, volume.getDiskId()) && 119 volume.getType() == VolumeInfo.TYPE_PRIVATE) { 120 storageManager.forgetVolume(volume.getFsUuid()); 121 } 122 } 123 124 storageManager.partitionPublic(diskId); 125 126 LocalBroadcastManager.getInstance(this).sendBroadcast( 127 new Intent(ACTION_FORMAT_AS_PUBLIC) 128 .putExtra(DiskInfo.EXTRA_DISK_ID, diskId) 129 .putExtra(EXTRA_SUCCESS, true)); 130 } catch (Exception e) { 131 Log.e(TAG, "Failed to format " + diskId, e); 132 LocalBroadcastManager.getInstance(this).sendBroadcast( 133 new Intent(ACTION_FORMAT_AS_PUBLIC) 134 .putExtra(DiskInfo.EXTRA_DISK_ID, diskId) 135 .putExtra(EXTRA_SUCCESS, false)); 136 } 137 } 138 139 private void formatAsPrivate(String diskId) { 140 try { 141 final StorageManager storageManager = getSystemService(StorageManager.class); 142 storageManager.partitionPrivate(diskId); 143 final long internalBench = storageManager.benchmark(null); 144 145 final VolumeInfo privateVol = findVolume(storageManager, diskId); 146 final long privateBench; 147 if (privateVol != null) { 148 privateBench = storageManager.benchmark(privateVol.getId()); 149 } else { 150 privateBench = -1; 151 } 152 LocalBroadcastManager.getInstance(this).sendBroadcast( 153 new Intent(ACTION_FORMAT_AS_PRIVATE) 154 .putExtra(DiskInfo.EXTRA_DISK_ID, diskId) 155 .putExtra(EXTRA_INTERNAL_BENCH, internalBench) 156 .putExtra(EXTRA_PRIVATE_BENCH, privateBench) 157 .putExtra(EXTRA_SUCCESS, true)); 158 } catch (Exception e) { 159 Log.e(TAG, "Failed to format " + diskId, e); 160 LocalBroadcastManager.getInstance(this).sendBroadcast( 161 new Intent(ACTION_FORMAT_AS_PRIVATE) 162 .putExtra(DiskInfo.EXTRA_DISK_ID, diskId) 163 .putExtra(EXTRA_SUCCESS, false)); 164 } 165 } 166 167 private VolumeInfo findVolume(StorageManager storageManager, String diskId) { 168 final List<VolumeInfo> vols = storageManager.getVolumes(); 169 for (final VolumeInfo vol : vols) { 170 if (TextUtils.equals(diskId, vol.getDiskId()) 171 && (vol.getType() == VolumeInfo.TYPE_PRIVATE)) { 172 return vol; 173 } 174 } 175 return null; 176 } 177 178 private void unmount(String volumeId) { 179 try { 180 final long minTime = System.currentTimeMillis() + 3000; 181 182 final StorageManager storageManager = getSystemService(StorageManager.class); 183 final VolumeInfo volumeInfo = storageManager.findVolumeById(volumeId); 184 if (volumeInfo != null && volumeInfo.isMountedReadable()) { 185 Log.d(TAG, "Trying to unmount " + volumeId); 186 storageManager.unmount(volumeId); 187 } else { 188 Log.d(TAG, "Volume not found, skipping unmount"); 189 } 190 191 long waitTime = minTime - System.currentTimeMillis(); 192 while (waitTime > 0) { 193 try { 194 Thread.sleep(waitTime); 195 } catch (InterruptedException e) { 196 // Ignore 197 } 198 waitTime = minTime - System.currentTimeMillis(); 199 } 200 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_UNMOUNT) 201 .putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId) 202 .putExtra(EXTRA_SUCCESS, true)); 203 } catch (Exception e) { 204 Log.d(TAG, "Could not unmount", e); 205 LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(ACTION_UNMOUNT) 206 .putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId) 207 .putExtra(EXTRA_SUCCESS, false)); 208 } 209 } 210 } 211 } 212