Android 6.0权限和挂载OBB

Android 6.0 permissions and mounting OBB

我在 Android 6.0 及更高版本上遇到权限问题。

我正在开发的游戏使用了 WRITE_EXTERNAL_STORAGE 权限。在 Android 6.0 及更高版本上,必须显示要求用户允许的警报。

我收到了删除此警报的请求,这意味着游戏无法再使用 WRITE_EXTERNAL_STORAGE。

从技术上讲,游戏目前没有在任何地方写入和读取外部存储。所有保存和缓存文件都存储在特定于应用程序的目录中 (/storage/emulated/0/Android/data/com.company.productname) 但是,游戏必须读取(有时下载)存储在 /storage/emulated/0/Android/obb/com.company.productname 目录中的 OBB 文件。

游戏可以访问此目录,即使设备上没有 WRITE_EXTERNAL_STORAGE 或 READ_EXTERNAL_STORAGE:

但它似乎没有访问此目录的权限:

为了模拟从商店下载游戏,我什至使用 Alpha 测试对其进行了检查。

当(通过应用程序设置)授予存储权限后,一切运行正常。

OBB是如何挂载的?

我用的Unreal Engine4是用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 系列会惹出这样的麻烦?

根据上述评论主题在此提供答案。问题在于提供 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权限才能写入数据。

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

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

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

重启设备后一切正常。

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

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

感谢@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);
    }
}

请您检查您的商店申请是否具有书面许可。