Mounting an encrypted obb apk expansion file in Android - android

I have created an encrypted .obb file using the jobb tool. I use the following code to mount the obb file:
public void mountExpansion() {
final StorageManager storageManager = (StorageManager) getContext()
.getSystemService(Context.STORAGE_SERVICE);
String packageName = "name.of.the.package";
String filePath = Environment.getExternalStorageDirectory()
+ "/Android/obb/" + packageName + "/" + "main."
+ version + "." + packageName + ".obb";
final File mainFile = new File(filePath);
if (mainFile.exists()) {
Log.d("STORAGE", "FILE: " + filePath + " Exists");
} else {
Log.d("STORAGE", "FILE: " + filePath + " DOESNT EXIST");
}
String key = "thisIsMyPassword";
if (!storageManager.isObbMounted(mainFile.getAbsolutePath())) {
if (mainFile.exists()) {
if(storageManager.mountObb(mainFile.getAbsolutePath(), key,
new OnObbStateChangeListener() {
#Override
public void onObbStateChange(String path, int state) {
super.onObbStateChange(path, state);
Log.d("PATH = ",path);
Log.d("STATE = ", state+"");
expansionFilePath = storageManager.getMountedObbPath(path);
if (state == OnObbStateChangeListener.MOUNTED) {
expansionFilePath = storageManager
.getMountedObbPath(path);
Log.d("STORAGE","-->MOUNTED");
}
else {
Log.d("##", "Path: " + path + "; state: " + state);
}
}
}))
{
Log.d("STORAGE_MNT","SUCCESSFULLY QUEUED");
}
else
{
Log.d("STORAGE_MNT","FAILED");
}
} else {
Log.d("STORAGE", "Patch file not found");
}
}
}
I am getting the following output:
FILE: filePath Exists
SUCCESSFULLY QUEUED
But nothing inside onObbStateChangeListener is getting called. I am calling this function from a custom view and testing this on Nexus 4/ KitKat.
What could be the reason for this behaviour?

I know this question is old but this may help someone else.
The StorageManager stores the listener in a weak reference which means that, given your example code (an anonymous instance created in the method call), it is gone almost as soon as you create it and usually well before the mount completes. You must maintain a reference to the listener object in your own code until it is no longer needed.
Something like this should work:
public class MyClass {
...
private OnObbStateChangeListener mListener =
new OnObbStateChangeListener() {
#Override
public void onObbStateChange(String path, int state) {
// your code here
}
};
public void mountExpansion() {
...
if (storageManager.mountObb(mainFile.getAbsolutePath(), key, mListener)
{
Log.d("STORAGE_MNT","SUCCESSFULLY QUEUED");
}
else
{
Log.d("STORAGE_MNT","FAILED");
}
...
}
...
}
This particular quirk of obb mounting has existed since at least honeycomb to my knowledge.

There seems to be a bug with OBB mounting that was introduced with KitKat. Currently no workarounds are known however it should be fixed with the next incremental update.
http://code.google.com/p/android/issues/detail?id=61881

Related

DLNA/UPNP - Get all non-media files

I am new in working with UpNp and DLNA. I am using cling library and DLNA code from here. In this code, everything working perfectly but my need is something else. It is giving me all media folders(Videos, Audio and Images) from selected device in network. But what I want is non media files from selected device on the network i.e., .docx, .srt, .txt etc.
Here is some code portion:
upnpService.getControlPoint().execute(
new ContentBrowseActionCallback(ContentActivity.this,
service, createRootContainer(service), mContentList,
mHandler));
Root Container:
protected Container createRootContainer(Service service) {
Container rootContainer = new Container();
rootContainer.setId("0");
rootContainer.setTitle("Content Directory on "
+ service.getDevice().getDisplayString());
Log.e("createRootContainer", "service.getDevice().getDisplayString() : " + service.getDevice().getDisplayString());
return rootContainer;
}
ContentBrowseActionCallback:
public class ContentBrowseActionCallback extends Browse {
private static Logger log = Logger
.getLogger(ContentBrowseActionCallback.class.getName());
private Service service;
private Container container;
private ArrayList<ContentItem> list;
private Activity activity;
private Handler handler;
public ContentBrowseActionCallback(Activity activity, Service service,
Container container, ArrayList<ContentItem> list, Handler handler) {
super(service, container.getId(), BrowseFlag.DIRECT_CHILDREN, "*", 0,
null, new SortCriterion(true, "dc:title"));
this.activity = activity;
this.service = service;
this.container = container;
this.list = list;
this.handler = handler;
}
public void received(final ActionInvocation actionInvocation,
final DIDLContent didl) {
log.fine("Received browse action DIDL descriptor, creating tree nodes");
activity.runOnUiThread(new Runnable() {
public void run() {
try {
list.clear();
// Containers first
for (Container childContainer : didl.getContainers()) {
Log.e("Item", "childContainer : " + childContainer.getTitle());
log.fine("add child container "
+ childContainer.getTitle());
list.add(new ContentItem(childContainer, service));
}
// Now items
for (Item childItem : didl.getItems()) {
Log.e("Item", "childItem : " + childItem.getTitle());
log.fine("add child item" + childItem.getTitle());
list.add(new ContentItem(childItem, service));
}
} catch (Exception ex) {
log.fine("Creating DIDL tree nodes failed: " + ex);
actionInvocation.setFailure(new ActionException(
ErrorCode.ACTION_FAILED,
"Can't create list childs: " + ex, ex));
failure(actionInvocation, null);
handler.sendEmptyMessage(ContentActivity.CONTENT_GET_FAIL);
}
ConfigData.listPhotos.clear();
Iterator iterator = didl.getItems().iterator();
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
Log.e("received", "item.getTitle() : " + item.getTitle());
ContentItem contentItem = new ContentItem(item,
ContentBrowseActionCallback.this.service);
if ((contentItem.getItem().getTitle().toString() != null)
&& (contentItem.getItem().getResources() != null)) {
List list = contentItem.getItem().getResources();
if ((list.size() != 0)
&& (((Res) list.get(0)).getProtocolInfo() != null)
&& (((Res) list.get(0)).getProtocolInfo()
.getContentFormat() != null)) {
Log.e("received", "list.get(0).getProtocolInfo() : " + ((Res) list.get(0)).getProtocolInfo());
Log.e("received", "list.get(0).getProtocolInfo().getContentFormat() : " + ((Res) list.get(0)).getProtocolInfo().getContentFormat());
Log.e("received", "list.get(0).getProtocolInfo().getContentFormat().substring() : " + ((Res) list.get(0)).getProtocolInfo().getContentFormat().substring(0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/")));
if (((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.substring(
0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/"))
.equals("image")) {
ConfigData.listPhotos
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
} else if (((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.substring(
0,
((Res) list.get(0))
.getProtocolInfo()
.getContentFormat()
.indexOf("/"))
.equals("audio")) {
ConfigData.listAudios
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
} else {
Log.e("received", "item : " + item.getTitle());
ConfigData.listVideos
.add(new ContentItem(
item,
ContentBrowseActionCallback.this.service));
}
}
}
}
handler.sendEmptyMessage(ContentActivity.CONTENT_GET_SUC);
}
});
}
public void updateStatus(final Status status) {
}
#Override
public void failure(ActionInvocation invocation, UpnpResponse operation,
final String defaultMsg) {
}
}
I googled a lot about this point but nothing in hand. I got few ideas about container id and how it works.
Please let me know how to achieve this scenario. I also don't have much idea about how DLNA and UPNP works. Please also help me to understand DLNA and UPNP better.
Thank You!
Ok, After diving into code from more than a week, I got basic idea how DLNA and UPNP works. How callbacks are made and how it fetches media files from device. So, now I am able to answer my own question.
HOW TO GET NON MEDIA FILES:
Whenever discovery service found your own device, it will prepare media server which will fetch all media from your device. First it creates a Node for specific type of media.e.g., Videos, Photos, Audios etc. Here is the code:
ContentNode rootNode = ContentTree.getRootNode();
// Video Container
Container videoContainer = new Container();
videoContainer.setClazz(new DIDLObject.Class("object.container"));
videoContainer.setId(ContentTree.MY_FOLDER_ID);
videoContainer.setParentID(ContentTree.ROOT_ID);
videoContainer.setTitle("My Folder Name");
videoContainer.setRestricted(true);
videoContainer.setWriteStatus(WriteStatus.NOT_WRITABLE);
videoContainer.setChildCount(0);
rootNode.getContainer().addContainer(videoContainer);
rootNode.getContainer().setChildCount(
rootNode.getContainer().getChildCount() + 1);
ContentTree.addNode(ContentTree.MY_FOLDER_ID, new ContentNode(
ContentTree.MY_FOLDER_ID, videoContainer));
This way you can add your own folder.
To fetch files you need to use Cursor to get files from your device. Something like this:
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
+ MediaStore.Files.FileColumns.MEDIA_TYPE_NONE;
Uri externalUri = MediaStore.Files.getContentUri("external");
String[] filePathColumn = {MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA};
Cursor cursor = contentResolver.query(externalUri, filePathColumn,
selection, null, null);
cursor.moveToFirst();
do {
String id =
cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
String path =
cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
String serverPath = "http://" + mediaServer.getAddress() + "/"
+path;
} while (cursor.moveToNext());
cursor.close();
Thus, serverPath will become your file path and you can access your file from another device. You can filter your files using file extension.
I must say this is huge project which I am using, so there are many changes I did to achieve my requirement. But, this answer justify my question.
Thank You!

Can I delay the instabug IntroDialog?

In my Application class I have this method, that I call on the onCreate of my Application:
public void initInstabug() {
try {
instabug = new Instabug.Builder(this, "45c752889689ac2ae0840d11e2a5f628")
.setDebugEnabled(true)
.setEmailFieldRequired(true)
.setFloatingButtonOffsetFromTop(400)
.setShouldShowIntroDialog(true)
.setColorTheme(IBGColorTheme.IBGColorThemeLight)
.setCommentFieldRequired(true)
.setInvocationEvent(IBGInvocationEvent.IBGInvocationEventShake)
.build();
instabug.setPrimaryColor(getResources().getColor(R.color.background));
instabug.setPreSendingRunnable(new Runnable() {
#Override
public void run() {
Log.i("","instabug entered pre sending runnable");
String[] files = new String[2];
files[0] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log.txt";
files[1] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log2.txt";
Compress compress = new Compress(files, Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
compress.zip(new CrudStateCallback() {
#Override
public void onResponse(String string) {
Log.i("", "instabug ended making the archive");
}
});
}
});
File file = new File(Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
Uri uri = Uri.fromFile(file);
instabug.setFileAttachment(uri, null);
}catch (Exception e){
Log.e("","error instabug:" + e.getMessage());
Utils.appendLog("In case instabug crashes asyncTask process, will not crash app",true);
}
}
My Issue is that I have the login page, first, and the popup always comes up while I ussually write my login info (email, password).
Is it possible to delay the introDialog, so it will appear only when another activity is open?
I tried initialising my instabug instance with this code in Application, where .setShouldShowIntroDialog(FALSE) is set to false, as you can see. And then in the timeline I reinit it with .setShouldShowIntroDialog(true). But it doesn't show at all like this.
You can try to use Instabug.showIntroMessage() in your activity and set the .setShouldShowIntroDialog() to false.

Android Amazon S3 Uploading Crash

I'm trying to figure out what is the cause of this crash when uploading on Amazon S3 bucket.
Log is:
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.amazonaws.mobileconnectors.s3.transferutility.TransferService$NetworkInfoReceiver.isNetworkConnected()' on a null object reference
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService.execCommand(TransferService.java:287)
at com.amazonaws.mobileconnectors.s3.transferutility.TransferService$UpdateHandler.handleMessage(TransferService.java:224)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.os.HandlerThread.run(HandlerThread.java:61)
Is there is something wrong on my code?
public AmazonTransferUtility uploadFileToAmazonS3(String data, Date date){
generateTextFileFromString(data, date);
File jsonFile = new File(getDataPath(), textName);
TransferObserver observer = transferUtility.upload(
textBucketName,
mUUID + File.separator + date.getTime() + textName ,
jsonFile
);
mListener.onAsyncStart();
observer.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
try {
if (state.toString().equals("COMPLETED")) {
deleteFile(textName);
if (mListener != null) {
JSONObject result = new JSONObject();
result.put("result", state.toString());
mListener.onAsyncSuccess(result);
}
}
else if (state.toString().equals("FAILED") ||
state.toString().equals("UNKNOWN")
){
mListener.onAsyncFail(id, state.toString());
}
else{
Log.i(TAG, "S3 TransferState :" + state.toString());
}
}catch (JSONException e){
Log.e(TAG, e.getLocalizedMessage());
}
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
if (bytesCurrent == bytesTotal){
Log.i(TAG, "Completed");
}
else{
Log.i(TAG, "Current bytes: " + bytesCurrent + " Of bytesTotal : " + bytesTotal);
}
}
#Override
public void onError(int id, Exception ex) {
mListener.onAsyncFail(id,ex.getMessage());
}
});
return this;
}
And if ever how can I catch this error so that my app stop crashing and just cancel my uploading task.
BTW.
That crash is intermittent and ratio is 1 out of 5 successful sync
This doesn't appear to be something you are doing, it's inside the AWS SDK code. The implication of that NPE is a flaky network. It's been reported to Amazon on github (and confirmed in another ticket) and it appears rolling back one version in the SDK (v2.2.13) may help.
That also makes sense given the changes made in 2.2.14, which are related to S3 transfer and the network.
I'd suggest following those tickets (please don't +1). It's reasonable to expect they will fix it within a week.
Here's a workaround until the bug is fixed, just fire this up in your application's onCreate, or well before any upload activity starts:
/**
* work around for a bug:
* http://stackoverflow.com/questions/36587511/android-amazon-s3-uploading-crash
*/
public static void startupTranferServiceEarlyToAvoidBugs(Context context) {
final TransferUtility tu = new TransferUtility(
new AmazonS3Client((AWSCredentials)null),
context);
tu.cancel(Integer.MAX_VALUE - 1);
}
essentially what this does is tell the TransferService to start up and initialize it's member variables so that it doesn't enter the condition where it tries to service commands before it's ready to.

Android: Failed to ensure directory

I have been using "Environment.getExternalStorage()" to store and manage files. And there is no warning message from logcat with that method and works greatly fine.
But, My project needs to use method "Context.getExternalFilesDir(String type)" and there is a warning message
ContextImpl:Failed to ensure directory: /storage/external_SD/Android/data/(package name)/files
Fortunately that File object works fine(reading or write or making folder works, too).
But I want to know how to resolve that warning message. Do I miss something?
You should know how the warning message comes up.
The getExternalFilesDir(String type) will call getExternalFilesDirs(String type) (notice the 's' at the final of the second method name).
The getExternalFilesDirs(String type) will find all dirs of the type, and calls ensureDirsExistOrFilter() at the end to ensure the directories exist.
If the dir can't be reached, it will print a warning!
Log.w(TAG, "Failed to ensure directory: " + dir);
dir = null;
So, if your device has two sdcard paths, it will produce two dirs. If one is not available, the warning will come up.
The conclusion is the warning does not need to be fixed.
If you have code that is iterating files, calling this API many times, this warning can cause log pollution. To solve this (since the warning is actually benign) you can create a wrapper class that stores the result of calling getExternalFilesDir / getExternalCacheDir and subsequently returns the stored value instead of calling the API. In this way, at least you will only ever see this message once.
I follow the getExternalFilesDir() source
/**
* Ensure that given directories exist, trying to create them if missing. If
* unable to create, they are filtered by replacing with {#code null}.
*/
private File[] ensureExternalDirsExistOrFilter(File[] dirs) {
File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {
if (!dir.mkdirs()) {
// recheck existence in case of cross-process race
if (!dir.exists()) {
// Failing to mkdir() may be okay, since we might not have
// enough permissions; ask vold to create on our behalf.
final IMountService mount = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
try {
final int res = mount.mkdirs(getPackageName(), dir.getAbsolutePath());
if (res != 0) {
Log.w(TAG, "Failed to ensure " + dir + ": " + res);
dir = null;
}
} catch (Exception e) {
Log.w(TAG, "Failed to ensure " + dir + ": " + e);
dir = null;
}
}
}
}
result[i] = dir;
}
return result;
}
immediate use Environment.getExternalStorageDirectory() get ExternalDirs
public final class StorageUtil {
public static final String DIR_ANDROID = "Android";
private static final String DIR_DATA = "data";
private static final String DIR_FILES = "files";
private static final String DIR_CACHE = "cache";
#Nullable
public static synchronized File getExternalStorageAppFilesFile(Context context, String fileName) {
if (context == null) return null;
if (fileName == null) return null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dirs = buildExternalStorageAppFilesDirs(Environment.getExternalStorageDirectory().getAbsolutePath(), context.getPackageName());
return new File(dirs, fileName);
}
return null;
}
public synchronized static File buildExternalStorageAppFilesDirs(String externalStoragePath, String packageName) {
return buildPath(externalStoragePath, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
public synchronized static File buildPath(String base, String... segments) {
File cur = new File(base);
for (String segment : segments) {
cur = new File(cur, segment);
}
return cur;
}
}

Creating a folder inside a folder in google drive android

I want to integrate Google drive with my app I have registered my app in Google Developers Console. I got a sample from https://github.com/googledrive/android-demos .By this i am able to create a file,folder in Google drive's root folder but the problem is I couldn't create a file or folder inside an existing folder. In such case i got a toast "Cannot find DriveId. Are you authorized to view this file?" ie I cannot get the driveID
public class CreateFolderInFolderActivity extends BaseDemoActivity {
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
Drive.DriveApi.fetchDriveId(getGoogleApiClient(), EXISTING_FOLDER_ID)
.setResultCallback(idCallback);
}
final ResultCallback<DriveIdResult> idCallback = new ResultCallback<DriveIdResult>() {
#Override
public void onResult(DriveIdResult result) {
if (!result.getStatus().isSuccess()) {
showMessage(result.getStatus().toString());
showMessage("Cannot find DriveId. Are you authorized to view this file?");
return;
}
DriveFolder folder = Drive.DriveApi
.getFolder(getGoogleApiClient(), result.getDriveId());
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyNewFolder").build();
folder.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(createFolderCallback);
}
};
final ResultCallback<DriveFolderResult> createFolderCallback = new
ResultCallback<DriveFolderResult>() {
#Override
public void onResult(DriveFolderResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Problem while trying to create a folder");
return;
}
showMessage("Folder successfully created");
}
};
}
I cannot find any proper documentation for this.
Plz help me where i am going wrong or Whether I Have to include any other permissions
You can take a peek here: in the 'createTree()' method, there is a creation of folder within a folder.
There are 3 different drive ID entities in the new Google Drive Android API (GDAA)
the object of type DriveID - the one you get from methods and use in your code
a string you get from encodeToString() and pass to decodeFromString() - used to save within the app (caching for instance)
a string you get from getResourceId() and pass to fetchDriveId() - the one you see in the html address of a file.
Both 2 and 3 identifiers are strings, so they may be confused. Identifier 2 is faster when retrieving Drive ID (via decodeFromString()). Identifier 3 is slower to retrieve (via fetchDriveId()), but usefull if you need to take your ID elsewhere (Apps Script, for instance).
Please see also: SO 21800257
What is EXISTING_FOLDER_ID? If you are trying to just run the sample straight out without having made any changes, this won't work.
You need to change EXISTING_FOLDER_ID to the resource id of a folder that your app has access to. This could be a folder that your app created in the root.
first create folder using creatreeTree()
then run a search query to get id of create public static ArrayList<ContentValues> search(String prnId, String titl, String mime) {
ArrayList<ContentValues> gfs = new ArrayList<>();
if (mGOOSvc != null && mConnected) try {
// add query conditions, build query
String qryClause = "'me' in owners and ";
if (prnId != null) qryClause += "'" + prnId + "' in parents and ";
if (titl != null) qryClause += "title = '" + titl + "' and ";
if (mime != null) qryClause += "mimeType = '" + mime + "' and ";
qryClause = qryClause.substring(0, qryClause.length() - " and ".length());
Drive.Files.List qry = mGOOSvc.files().list().setQ(qryClause)
.setFields("items(id,mimeType,labels/trashed,title),nextPageToken");
String npTok = null;
if (qry != null) do {
FileList gLst = qry.execute();
if (gLst != null) {
for (File gFl : gLst.getItems()) {
if (gFl.getLabels().getTrashed()) continue;
gfs.add( UT.newCVs(gFl.getTitle(), gFl.getId(), gFl.getMimeType()));
} //else UT.lg("failed " + gFl.getTitle());
npTok = gLst.getNextPageToken();
qry.setPageToken(npTok);
}
} while (npTok != null && npTok.length() > 0); //UT.lg("found " + vlss.size());
} catch (Exception e) { UT.le(e); }
return gfs;
}
when you get folder id use this code to create folder in folder ` public static ParentReference insertFileIntoFolder(Drive service, String folderId,
String folderName) throws IOException {
// Log.e("founddd",id);
File fileMetadata = new File();
fileMetadata.setParents(Collections.singletonList(new ParentReference().setId(folderId == null ? "root" : folderId)));
fileMetadata.setTitle(folderName);
fileMetadata.setMimeType("application/vnd.google-apps.folder");
File file = mGOOSvc.files().insert(fileMetadata).execute();
System.out.println("Folder ID: " + file.getId());
strChildFolder = file.getId();
return null;
}`

Categories

Resources