Status: not solved yet
Problem
Once I build and install an app with GearVR it asks by default:
XXX is requesting permission to access photos, media and files on your device. Allow?
Allow / Deny
I don't need my app to access any of those above. How can I get rid of this programmatically?
Edit 1
It seems that Unity automatically adds READ_EXTERNAL_STORAGE to any build for some reason. Stripping out the permission doesn't help in newer versions of Unity as the build fails with the message:
Unable to merge android manifests...
Edit 2
The OVRPlugin causes this issue and at the moment.
https://forum.unity3d.com/threads/how-to-remove-android-permission-read_external_storage.429369/
Unity forums say it is unlikely to fix. Maybe there are still some workarounds?
Edit 3
Oculus is working on it, we should receive a fix in February
https://forums.oculus.com/developer/discussion/42465/is-android-permission-read-external-storage-a-must-when-using-unity#latest
I stumble upon this some time ago and still not sure why Unity builds ask for those permissions. Anyway to get rid of them you can use an editor script as a workaround:
protected void BuildAPKCustom(string buildPath)
{
bool manifestFileExists = false;
if(File.Exists(Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml")))
{
manifestFileExists = true;
File.Copy(Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml"), Path.Combine(Application.dataPath, "Plugins/Android/tmpManifestFile.xml"));
File.Delete(Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml"));
}
string[] levels = new string[EditorSceneManager.sceneCount];
for (int i = 0; i < levels.Length; i++)
{
levels[i] = EditorSceneManager.GetSceneAt(i).path;
}
BuildPipeline.BuildPlayer(levels, buildPath, BuildTarget.Android, BuildOptions.None);
XNamespace android = "http://schemas.android.com/apk/res/android";
string generatedXMLFilePath = Path.Combine(Application.dataPath, "../Temp/StagingArea/AndroidManifest.xml");
XDocument doc = XDocument.Load(generatedXMLFilePath);
IEnumerable<XElement> permissionElements = doc.Root.Elements("uses-permission");
foreach(XElement permission in permissionElements)
{
int potentialIndex = PERMISSIONS_NAMES_TO_DELETE.IndexOf(permission.Attribute(android + "name").Value.Replace("android.permission.", ""));
if(potentialIndex >= 0)
{
UnityEngine.Debug.Log("Permission deleted : " + PERMISSIONS_NAMES_TO_DELETE[potentialIndex]);
permission.Remove();
}
}
XElement overwritenPermission;
for(int i = 0; i < PERMISSIONS_NAMES_TO_DELETE.Length; i++)
{
overwritenPermission = new XElement("uses-permission");
overwritenPermission.Add(new XAttribute(android + "name", "android.permission." + PERMISSIONS_NAMES_TO_DELETE[i]));
overwritenPermission.Add(new XAttribute(android + "maxSdkVersion", "18"));
doc.Element("manifest").Add(overwritenPermission);
}
if(!Directory.Exists(Path.Combine(Application.dataPath, "Plugins/Android")))
{
Directory.CreateDirectory(Path.Combine(Application.dataPath, "Plugins/Android"));
}
doc.Save(Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml"));
BuildPipeline.BuildPlayer(levels, buildPath, BuildTarget.Android, BuildOptions.None);
if(manifestFileExists)
{
File.Delete(Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml"));
File.Copy(Path.Combine(Application.dataPath, "Plugins/Android/tmpManifestFile.xml"), Path.Combine(Application.dataPath, "Plugins/Android/AndroidManifest.xml"));
File.Delete(Path.Combine(Application.dataPath, "Plugins/Android/tmpManifestFile.xml"));
}
Process.Start(Path.GetDirectoryName(buildPath));
}
(PERMISSIONS_NAMES_TO_DELETE being a string[] of the permissions names to delete. To get the permissions names list you can look here)
What it does is:
get the previous AndroidManifest.xml file (if it exists) in /Android/Plugins/ folder, copy it to a temporary one and delete it
perform a first build to let Unity generate an AndroidManifest.xml file on his own
edit the manifest to "delete" unnecessary permissions
save the edited manifest to /Android/Plugins/
perform a second build with a correctly set manifest
The idea behind the permission deletion is to set the maximum API level to such a lower level, the permissions will not be usable (please note the permission MUST exists at this API level).
Hope this helps,
Related
I'm trying to crawl the entire file system of an android device, both directories and files, without the benefit of NIO, to build a tree of it. If I had NIO then I could use WalkTree or similar, but I don't.
The problem I am having (on the Nexus 5 API 23 x86 emulator) is in /sys/bus/pci/devices and possibly other directories (eg /proc/self) - it doesn't complete before the app times out/quits/crashes (unknown which), possibly getting into some kind of loop or something (the path may change in a repetitive fashion but the canonical path varies little or not at all) .
However if I rule out Symbolic links then that problem goes away but I get what is only some of the files on the device rather than all - for example lacking files under /data (or /data/media/0) and those files not showing up elsewhere - not to mention it looks completely different from the file system that most file managers show. The former is strange as I'd understood Symbolic Links pointed to files and folders that were still present in the file system, but just made them look as if they were elsewhere.
What's the solution? Do I have to code exceptions or special handling for /sys/bus/pci/devices, /proc/self and others? I'd prefer to keep Symbolic Links being followed if I can, and I'd prefer to crawl as many files and folders as I can (so starting in a sub-folder is not preferred).
And a few related questions that might affect the approach I eventually take - if I DO keep SymLinks then does that mean that some things will be crawled twice or more? Is there a way to avoid that? Is there a way to detect when something is the TARGET of a SymLink, other than following the SymLink and checking the CanonicalPath?
Here's my code:
I get the root (I understand that in Android, the first and likely only root is the valid one):
File[] roots = File.listRoots();
String rootPath = "";
try {
rootPath = roots[0].getCanonicalPath();
} catch (IOException e) {
// do something
}
Then I start the crawl (note the boolean to choose whether to ignore simlinks or not):
try {
// check if the rootPath is null or empty, and then...
File rootFile = new File(rootPath);
rootNode = new FileFolderNode(rootFile, null, true, false); // last param may be true to ignore sim links
//FileFolderNode(String filePath, FileFolderNode parent, boolean addChildren, boolean ignoreSimLinks)
} catch (Exception e) {
// do something
}
That uses the FileFolderNode, which has constructor:
public FileFolderNode(File file, FileFolderNode parent, boolean addChildren, boolean ignoreSimLinks) throws IOException {
if (file == null)
throw new IOException("File is null in new FileFolderNode");
if (!file.exists())
throw new IOException("File '" + file.getName() + "' does not exist in new FileFolderNode");
// for now this uses isSymLink() from https://svn.apache.org/repos/asf/commons/_moved_to_git/io/trunk/src/main/java/org/apache/commons/io/FileUtils.java adjusted a bit to remove Java 7 and Windows mentions
if (!ignoreSimLinks)
if (FileUtils.isSymlink(file))
return;
this.name = file.getName();
if (this.name.equals("") && ! file.getCanonicalPath().equals("/"))
throw new IOException("Name is empty in new FileFolderNode");
this.isDirectory = file.isDirectory();
if (this.isDirectory) {
this.children = new ArrayList<FileFolderNode>();
if (addChildren) {
File[] files = file.listFiles();
if (files == null) {
// do something
} else {
// add in children
for (File f : files) {
FileFolderNode child = null;
try {
child = new FileFolderNode(f, this, addChildren, ignoreSimLinks);
} catch (Exception e) {
child = null;
}
if (child != null)
children.add(child);
}
}
}
}
}
Given the lack of answers here, I've broken this question down into areas needing clarification, and am trying to get answers to those - please do see if you can help with those:
Get Android Filing System root
Android SymLinks to hidden or separate locations or partitions
Avoiding Android Symbolic Link loop
We have 3rd party program to Download, delete and upload *.db files from Android to Windows. Everything work prefectly but with Windows 10 1703, the Upload stopped working (I mean From Windows 10 to the Android Device). My error is in french but the translation must be : "Attempted read or write protected memory. This often [...]". The strange part is that the Download and Delete still work. Oh, and everything works with Windows 10 1607.
The error pop at the line : "targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten); "
I was looking to upload the file in many folder on my Android Devide, but nothing work. Also, I tryed few type of device, LG phone, S7 phone, CT4 Device.
I'm looking into Phone permission and Windows 10 Permissions, trying to run with admin rights or compatibility modes. But I just cannot figure why it stopped working.
I'm really lost and some answer or guideline might be very usefull. Thanks a lot!
There is the Upload code (from Windows to Android) using PortableDeviceApiLib.dll :
public void TransferContentToDevice(string fileName, string parentObjectId)
{
IPortableDeviceContent content;
this._device.Content(out content);
parentObjectId = parentObjectId + "";
IPortableDeviceValues values =
GetRequiredPropertiesForContentType(fileName, parentObjectId);
PortableDeviceApiLib.IStream tempStream;
uint optimalTransferSizeBytes = 0;
content.CreateObjectWithPropertiesAndData(
values,
out tempStream,
ref optimalTransferSizeBytes,
null);
System.Runtime.InteropServices.ComTypes.IStream targetStream =
(System.Runtime.InteropServices.ComTypes.IStream)tempStream;
try
{
using (var sourceStream =
new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var buffer = new byte[optimalTransferSizeBytes];
int bytesRead;
do
{
bytesRead = sourceStream.Read(buffer, 0, (int)optimalTransferSizeBytes);
IntPtr pcbWritten = IntPtr.Zero;
if (bytesRead < (int)optimalTransferSizeBytes)
{
targetStream.Write(buffer, bytesRead, pcbWritten);
}
else
{
targetStream.Write(buffer, (int)optimalTransferSizeBytes, pcbWritten);
}
} while (bytesRead > 0);
}
targetStream.Commit(0);
}
finally
{
Marshal.ReleaseComObject(tempStream);
}
}
When I plug my device into big computer I see the following picture
How to find these (two) directories programmatically from withing Android application?
UPDATE
I wrote utility class to deduce roots. Unfortunately, it works for minSdkVersion=19
public class RootsUtil {
private final static String seed = Environment.DIRECTORY_PICTURES;
public final static File[] getRoots(Context context) {
File[] paths = context.getExternalFilesDirs(seed);
if( paths.length <= 1 ) {
return new File[] { Environment.getExternalStorageDirectory() };
}
else {
while(true) {
int count = 1;
for (int i = 1; i < paths.length; ++i) {
if (paths[0].getName().equals(paths[i].getName())) {
count++;
}
}
if( count==paths.length ) {
for (int i = 0; i < paths.length; ++i) {
paths[i] = paths[i].getParentFile();
}
}
else {
break;
}
}
return paths;
}
}
}
The question persists: are there any solutions for at least SDK=15?
P.S.
People downvoting this (absolutely normal) question: you are just declaring yourselves a clowns.
How to find these (two) directories programmatically from withing Android application?
You don't.
The one labeled "Phone" presumably is what the Android SDK refers to as external storage. I say "presumably" because device manufacturers seem to change this label — I usually see it called "Internal" or "Internal storage". To get the root of external storage, use Environment.getExternalStorageDirectory(). Note that this requires that you hold the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions, which includes asking for those permissions at runtime.
The one labeled "Card" presumably is referring to some removable media. You cannot work with the root directory of removable storage.
Maybe this will help you:
https://stackoverflow.com/a/40123073/5002496
thanks to that method you can list all mounted external storages paths. I am using it in my project to store data in sd-card and tested it on more than 20 devices.
I've problem with building search request on android.
ArrayList<ParseQuery<Entity>> queriesByCriteria = new ArrayList<>();
queriesByCriteria.add(ParseQuery.getQuery(Entity.class).whereContains("userName", criteria));
queriesByCriteria.add(ParseQuery.getQuery(Entity.class).whereContains("locationName", criteria));
queriesByCriteria.add(ParseQuery.getQuery(Entity.class).whereContains("descriptionBefore", criteria));
queriesByCriteria.add(ParseQuery.getQuery(Entity.class).whereContains("descriptionAfter", criteria));
ParseQuery<Entity> combinedQuery = ParseQuery.or(queriesByCriteria)
.orderByDescending("createdAt")
.whereEqualTo("done", true);
float mapRadius;
int mapUnits = preferences.getMapUnits();
if (mapUnits == MapUnitType.MAP_UNIT_KILOMETER) {
mapRadius = (preferences.getMapRadius());
} else {
mapRadius = 1.6f * (preferences.getMapRadius());
}
entities = combinedQuery
.whereWithinKilometers("location", new ParseGeoPoint(latLng.getLatitude(), latLng.getLongitude()), mapRadius)
.find();
So find() throws exception "com.parse.ParseException: internal error".
Version of Parse SDK is 1.7.1
Is it bug of parse.com or I do something wrong?
Yes, internal errors in SDK's refer to errors which are internal and not meant to be exposed to user's of the SDK. In this case, there is an edge case not handled internally by the find() method. My recommendation would be to go to the Parse suppport page and report this as a bug.
I am using Eclipse for Android SDK on Linux, and searching for a way to add the date and starttime of the compilation to one of the xml files. I like to see on the device which build version I am using, without updating this information before every compile step manually.
So far by searching the net I only found hints like "use ant".
I guess I have to use /proc/driver/rtc which is a dynamic "file" provided by the linux kernel that contains real time updated lines with colon separated text named for example "rtc_date" and "rtc_time". Including it and use the app on the device to get the information extracted.
Is there a better way? Like having eclipse either by knowing the time or stripping the information from proc and putting it at compile time in the xml file?
Its my first time using eclipse, so please excuse if I asked something obvious or impossible.
Regards
ct
I am using this code to get application build time. I know this is not outputting to an XML, but if you are trying to get when the app was build, this should work.
private long getAppBuildTime() {
if(cachedAppBuildTime == null) {
try{
ApplicationInfo ai = appContext.getPackageManager().getApplicationInfo(appContext.getPackageName(), 0);
ZipFile zf = new ZipFile(ai.sourceDir);
ZipEntry ze = zf.getEntry("classes.dex");
cachedAppBuildTime = ze.getTime();
log("app build time " + cachedAppBuildTime);
}catch(Throwable t){
return 1;
}
}
return cachedAppBuildTime;
}
The appContext variable in the code is obtained via context.getApplicationContext()
I use the same strategy as yigit except I prefer the MANIFEST.MF file.
This one is regenerated even if a layout is modified (which is not the case for classes.dex).
It result in the following code:
private long mAppBuildTime = -1;
public long getAppBuildTime() {
if (mAppBuildTime == -1) {
try{
ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), 0);
ZipFile zf = new ZipFile(ai.sourceDir);
ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
mAppBuildTime = ze.getTime();
zf.close();
}catch(Exception e){
}
}
return mAppBuildTime;
}