Android 6.0权限与挂载OBB

9
我在Android 6.0及以上版本中,遇到了权限问题。
我正在开发的游戏使用了WRITE_EXTERNAL_STORAGE权限。在Android 6.0及以上版本中,需要显示提示框来要求用户允许此权限。
现在有人要求去掉这个提示框,也就是说,游戏不能再使用WRITE_EXTERNAL_STORAGE权限了。
从技术上讲,游戏目前没有在任何地方读写外部存储器。所有保存和缓存文件都存储在应用程序特定目录(/storage/emulated/0/Android/data/com.company.productname)中。但是,游戏必须读取(有时还要下载)OBB文件,该文件存储在/storage/emulated/0/Android/obb/com.company.productname目录中。
即使在没有WRITE_EXTERNAL_STORAGE或READ_EXTERNAL_STORAGE权限的设备上,游戏仍然可以访问此目录:
- Motorola Nexus 6 - LG Nexus 5
但是,在以下设备上似乎无法访问此目录:
- Samsung Galaxy S5、S6和S7 - Nvidia Shield平板电脑
我甚至使用Alpha测试模拟从商店下载游戏来检查它。
当通过应用设置授予对存储的权限后,一切都正常运行。
OBB文件如何挂载?
我使用的虚幻引擎4使用C的open()函数来处理OBB文件。
int32 Handle = open(LocalPath, O_RDONLY);

其中LocalPath是obb文件的完整路径:/storage/emulated/0/Android/obb/com.company.productname/main.10003.com.company.productname.obb

问题如下:

  1. 游戏是否需要READ/WRITE_EXTERNAL_STORAGE权限才能读取和下载OBB文件?
  2. 如果是,那么我如何消除要求用户授权的警告?
  3. 如果不需要,那么为什么三星Galaxy S系列会出现这样的问题?

收到了关闭这个警报的请求。荒唐的请求。告诉他们!让他们去您应用的设置中切换权限。 - greenapps
你是如何挂载OBB文件的?并且你将什么作为“LocalPath”传递给UE4? - Larry Schiefer
另外,这些不同设备上运行的Android版本是哪些? - Larry Schiefer
@LarrySchiefer 所有这些设备都运行在Android 6.0.1上。LocalPath是一个指向/storage/emulated/0/Android/obb/com.company.productname/main.10003.com.company.productname.obb的const char*。 确切地说,它是getExternalStorageDirectory() + "/Android/obb/" + PackageName的组合。 如果您可以访问UE4 github,您可以检查源代码https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/Core/Private/Android/AndroidFile.cpp(MountOBB函数)。 - zompi
4个回答

10

好的,似乎问题被列在别的地方,但我找不到,因为我不知道这就是确切的问题...

https://code.google.com/p/android/issues/detail?id=197287

由于某种原因,API23将OBB的用户设置为root,而不是正确的用户,因此应用程序无法访问它。

重新启动设备后,一切都正常了。

目前还没有明确的解决方法。

感谢@Larry提供所有必要和有用的信息:)


2

请检查您的应用程序是否具有写入权限。


1
感谢@Larry指出正确的解决方案。
对于那些想知道为什么AOSP APK扩展支持库没有权限就无法工作的人,这里是修改后的APKExpansionSupport.java
package com.android.vending.expansion.zipfile;
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.io.File;
import java.io.IOException;
import java.util.Vector;

import android.content.Context;

public class APKExpansionSupport {
    public static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
        String packageName = ctx.getPackageName();
        Vector<String> ret = new Vector<>(2);
        File expPath = ctx.getObbDir();
        // Check that expansion file path exists
        if (expPath.exists()) {
            if ( mainVersion > 0 ) {
                String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb";
                File main = new File(strMainPath);
                if ( main.isFile() ) {
                    ret.add(strMainPath);
                }
            }
            if ( patchVersion > 0 ) {
                String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb";
                File main = new File(strPatchPath);
                if ( main.isFile() ) {
                    ret.add(strPatchPath);
                }
            }
        }
        String[] retArray = new String[ret.size()];
        ret.toArray(retArray);
        return retArray;
    }

    public static ZipResourceFile getResourceZipFile(String[] expansionFiles) throws IOException {
        ZipResourceFile apkExpansionFile = null;
        for (String expansionFilePath : expansionFiles) {
            if ( null == apkExpansionFile ) {
                apkExpansionFile = new ZipResourceFile(expansionFilePath);
            } else {
                apkExpansionFile.addPatchFile(expansionFilePath);
            }
        }
        return apkExpansionFile;
    }

    public static ZipResourceFile getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion) throws IOException{
        String[] expansionFiles = getAPKExpansionFiles(ctx, mainVersion, patchVersion);
        return getResourceZipFile(expansionFiles);
    }
}

0
根据上面的评论线程提供一个答案。问题在于提供LocalPath的方式。在Android中,不同制造商之间的文件系统路径不一致,您不应该对存储路径做任何假设。
因此,与其有这个(只是示例代码):
public class MyActivity extends Activity {
    static final String LocalPath = "/storage/emulated/0/Android/obb/com.company.productname/main.10003.com.company.p‌​roductname.obb ";
    ...
}

你需要像这样做:
public class MyActivity extends Activity {
    File mObb;
    ...

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate();
        ...

        mObb = new File(getObbDir(), getPackageName() + ".obb");

        //  Get the path for passing to UE4 later
        String LocalPath = mObb.getAbsolutePath();
    }

这将确保您的代码在给定设备的主要外部存储上获取正确的应用程序OBB位置。然后,您可以将OBB保存在那里,当您的应用程序启动UE4时,它可以直接指向该文件。

应用程序在主要外部存储器上的OBB(和其他文件)位置不需要权限来读取和写入该应用程序。如果外部存储器上的路径不是通过上述方法生成的路径,则需要WRITE_EXTERNAL_STORAGE权限才能写入数据。


1
谢谢你的提示,但是...我使用getObbDir()和getAbsolutePath()获取地址,它返回了.../storage/emulated/0/Android/obb/com.company.productname ;)我还检查了open方法的errno,它是EACCES 13 /* Permission denied */还有一件事...我通过将apk和obb放入Android/obb中创建的目录中并安装APK来安装游戏。当我将Obb放在存储的根目录中,然后通过Android文件管理器内部复制到正确的目录中,然后安装...然后它就可以访问... - zompi
UE4引擎是在您的应用程序进程内运行,还是作为单独的组件/包运行? - Larry Schiefer
UE4引擎正在应用程序中运行(这意味着引擎和游戏是同一个实体)。更神秘的是,我使用“getExternalStorageState”检查了文件,并收到了“MEDIA_MOUNTED”的结果。那么...它应该是可访问的,但不能通过C打开?我还通过Alpha测试安装了应用程序,出现了相同的问题。 - zompi
如果是通过 Play 商店或 adb 安装且使用 getObbDir() 方法创建,则应该没问题。如果使用常量字符串,则某些设备的挂载点不同会导致失败。 - Larry Schiefer
1
在经过相当长时间的搜索后,我在这里找到了问题 https://code.google.com/p/android/issues/detail?id=197287 和这里 http://comments.gmane.org/gmane.comp.handhelds.android.devel/244716 ,我可以确认已安装应用程序的用户是root用户,而不是普通用户。 - zompi
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接