d830becb4da325b1abac57dce2e7cccaf674cbc5
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / extension / api / device_capabilities / DeviceCapabilitiesStorage.java
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.core.internal.extension.api.device_capabilities;
6
7 import android.content.BroadcastReceiver;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.IntentFilter;
11 import android.os.Build.VERSION;
12 import android.os.Build.VERSION_CODES;
13 import android.os.Environment;
14 import android.os.StatFs;
15 import android.util.Log;
16 import android.util.SparseArray;
17
18 import java.io.File;
19
20 import org.json.JSONArray;
21 import org.json.JSONException;
22 import org.json.JSONObject;
23 import org.xwalk.core.internal.extension.XWalkExtensionContext;
24
25 class DeviceCapabilitiesStorage {
26     private static final String TAG = "DeviceCapabilitiesStorage";
27
28     private DeviceCapabilities mDeviceCapabilities;
29     private XWalkExtensionContext mExtensionContext;
30
31     private static int mStorageCount = 0;
32     // Holds all available storages.
33     private final SparseArray<StorageUnit> mStorageList = new SparseArray<StorageUnit>();
34
35     private boolean mIsListening = false;
36     private IntentFilter mIntentFilter = new IntentFilter();
37
38     class StorageUnit {
39         private int mId;
40         private String mName;
41         private String mType;
42         private long mCapacity;
43         private long mAvailCapacity;
44         private String mPath;
45
46         public StorageUnit(int id, String name, String type) {
47             mId = id;
48             mName = name;
49             mType = type;
50             mPath = "";
51             mCapacity = 0;
52             mAvailCapacity = 0;
53         }
54
55         public int getId() { return mId; }
56         public String getName() { return mName; }
57         public String getType() { return mType; }
58         public String getPath() { return mPath; }
59         public long getCapacity() { return mCapacity; }
60         public long getAvailCapacity() { return mAvailCapacity; }
61
62         public void setType(String type) { mType = type;}
63         public void setPath(String path) {
64           mPath = path;
65           updateCapacity();
66         }
67
68         public boolean isSame(StorageUnit unit) {
69             return mPath == unit.getPath();
70         }
71
72         public boolean isValid() {
73             if (mPath == null || mPath.isEmpty()) {
74                 mCapacity = 0;
75                 mAvailCapacity = 0;
76                 return false;
77             }
78
79             File file = new File(mPath);
80             return file.canRead();
81         }
82
83         @SuppressWarnings("deprecation")
84         public void updateCapacity() {
85             if (!isValid()) {
86                 return;
87             }
88
89             StatFs stat = new StatFs(mPath);
90             // FIXME(halton): After API level 18, use getTotalBytes() and
91             // getAvailableBytes() instead
92             long blockSize;
93             if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
94                 blockSize = stat.getBlockSizeLong();
95                 mCapacity = blockSize * stat.getBlockCountLong();
96                 mAvailCapacity = blockSize * stat.getAvailableBlocksLong();
97             } else {
98                 blockSize = stat.getBlockSize();
99                 mCapacity = blockSize * stat.getBlockCount();
100                 mAvailCapacity = blockSize * stat.getAvailableBlocks();
101             }
102         }
103
104         public JSONObject convertToJSON() {
105             JSONObject out = new JSONObject();
106
107             try {
108                 out.put("id", mId + 1); // Display from 1
109                 out.put("name", mName);
110                 out.put("type", mType);
111                 out.put("capacity", mCapacity);
112                 out.put("availCapacity", mAvailCapacity);
113             } catch (JSONException e) {
114                 return mDeviceCapabilities.setErrorMessage(e.toString());
115             }
116
117             return out;
118         }
119     }
120
121     private final BroadcastReceiver mStorageListener = new BroadcastReceiver() {
122         @Override
123         public void onReceive(Context context, Intent intent) {
124             String action = intent.getAction();
125             if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
126                 notifyAndSaveAttachedStorage();
127             }
128
129             if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)
130                 || Intent.ACTION_MEDIA_REMOVED.equals(action)
131                 || Intent.ACTION_MEDIA_BAD_REMOVAL.equals(action)) {
132                 notifyAndRemoveDetachedStorage();
133             }
134         }
135     };
136
137     public DeviceCapabilitiesStorage(DeviceCapabilities instance,
138                                      XWalkExtensionContext context) {
139         mDeviceCapabilities = instance;
140         mExtensionContext = context;
141
142         registerIntentFilter();
143
144         // Fetch the original storage list
145         initStorageList();
146     }
147
148     public JSONObject getInfo() {
149         JSONObject out = new JSONObject();
150         JSONArray arr = new JSONArray();
151         try {
152             for(int i = 0; i < mStorageList.size(); i++) {
153                 arr.put(mStorageList.valueAt(i).convertToJSON());
154             }
155             out.put("storages", arr);
156         } catch (JSONException e) {
157             return mDeviceCapabilities.setErrorMessage(e.toString());
158         }
159
160         return out;
161     }
162
163     private void initStorageList() {
164         mStorageList.clear();
165         mStorageCount = 0;
166
167         StorageUnit unit = new StorageUnit(mStorageCount, "Internal", "fixed");
168         unit.setPath(Environment.getRootDirectory().getAbsolutePath());
169         mStorageList.put(mStorageCount, unit);
170         ++mStorageCount;
171
172         // Attempt to add emulated stroage first
173         int sdcardNum = mStorageCount - 1; // sdcard count from 0
174         unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "fixed");
175         if (Environment.isExternalStorageRemovable()) {
176             unit.setType("removable");
177         }
178         unit.setPath(Environment.getExternalStorageDirectory().getAbsolutePath());
179
180         if (unit.isValid()) {
181             mStorageList.put(mStorageCount, unit);
182             ++mStorageCount;
183         }
184
185         // Then attempt to add real removable storage
186         attemptAddExternalStorage();
187     }
188
189     private void registerIntentFilter() {
190         mIntentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
191         mIntentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
192         mIntentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
193         mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
194         mIntentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
195         mIntentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
196         mIntentFilter.addDataScheme("file");
197     }
198
199     private boolean attemptAddExternalStorage() {
200         int sdcardNum = mStorageCount - 1;
201         StorageUnit unit = new StorageUnit(mStorageCount, new String("sdcard" + Integer.toString(sdcardNum)), "removable");
202         unit.setPath("/storage/sdcard" + Integer.toString(sdcardNum));
203
204         if (!unit.isValid()) {
205             return false;
206         }
207
208         for(int i = 0; i < mStorageList.size(); i++) {
209             if (unit.isSame(mStorageList.valueAt(i))) {
210                 return false;
211             }
212         }
213
214         mStorageList.put(mStorageCount, unit);
215         ++mStorageCount;
216         return true;
217     }
218
219     public void registerListener() {
220         if (mIsListening) {
221             return;
222         }
223
224         mIsListening = true;
225         mExtensionContext.getActivity().registerReceiver(mStorageListener, mIntentFilter);
226     }
227
228     public void unregisterListener() {
229         if (!mIsListening) {
230             return;
231         }
232
233         mIsListening = false;
234         mExtensionContext.getActivity().unregisterReceiver(mStorageListener);
235     }
236
237     private void notifyAndSaveAttachedStorage() {
238         if(!attemptAddExternalStorage()) {
239             return;
240         }
241
242         StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
243         JSONObject out = new JSONObject();
244         try {
245             out.put("reply", "attachStorage");
246             out.put("eventName", "storageattach");
247             out.put("data", unit.convertToJSON());
248
249             mDeviceCapabilities.broadcastMessage(out.toString());
250         } catch (JSONException e) {
251             mDeviceCapabilities.printErrorMessage(e);
252         }
253
254     }
255
256     private void notifyAndRemoveDetachedStorage() {
257         StorageUnit unit = mStorageList.valueAt(mStorageList.size() - 1);
258
259         if(unit.getType() != "removable") {
260             return;
261         }
262
263         JSONObject out = new JSONObject();
264         try {
265             out.put("reply", "detachStorage");
266             out.put("eventName", "storagedetach");
267             out.put("data", unit.convertToJSON());
268
269             mDeviceCapabilities.broadcastMessage(out.toString());
270             mStorageList.remove(unit.getId());
271             --mStorageCount;
272         } catch (JSONException e) {
273             mDeviceCapabilities.printErrorMessage(e);
274         }
275     }
276
277     public void onResume() {
278         // Fistly, check the lasted external storage is valid.
279         // If not, remove it and send "ondetached" event.
280         StorageUnit lastUnit = mStorageList.valueAt(mStorageList.size() - 1);
281         if(!lastUnit.isValid()) {
282             notifyAndRemoveDetachedStorage();
283         }
284
285         // Secondly, attmpt to add a possible external storage and send "onattached" event.
286         notifyAndSaveAttachedStorage();
287
288         registerListener();
289     }
290
291     public void onPause() {
292         unregisterListener();
293     }
294
295     public void onDestroy() {
296     }
297 }