OVRBundleManager.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. #if UNITY_EDITOR_WIN
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System;
  6. using System.Linq;
  7. using UnityEngine;
  8. using UnityEditor;
  9. #if UNITY_2018_1_OR_NEWER
  10. using UnityEditor.Build.Reporting;
  11. #endif
  12. public class OVRBundleManager
  13. {
  14. public const string TRANSITION_APK_VERSION_NAME = "OVRTransitionAPKVersion";
  15. private const int BUNDLE_CHUNK_SIZE = 30;
  16. private const string TRANSITION_SCENE_RELATIVE_PATH = "Scenes/OVRTransitionScene.unity";
  17. private const string BUNDLE_MANAGER_OUTPUT_PATH = "OVRAssetBundles";
  18. private const string BUNDLE_MANAGER_MASTER_BUNDLE = "OVRMasterBundle";
  19. private const string EXTERNAL_STORAGE_PATH = "/sdcard/Android/data";
  20. private const string ADB_TOOL_INITIALIZE_ERROR = "Failed to initialize ADB Tool. Please check Android SDK path in Preferences -> External Tools";
  21. private static string externalSceneCache;
  22. private static string transitionScenePath;
  23. private static string projectDefaultAppIdentifier;
  24. private static string projectDefaultVersion;
  25. private static ScriptingImplementation projectScriptImplementation;
  26. #if UNITY_2018_3_OR_NEWER
  27. private static ManagedStrippingLevel projectManagedStrippingLevel;
  28. #else
  29. private static StrippingLevel projectStrippingLevel;
  30. #endif
  31. private static bool projectStripEngineCode;
  32. public static void BuildDeployTransitionAPK(bool useOptionalTransitionApkPackage)
  33. {
  34. OVRBundleTool.PrintLog("Building and deploying transition APK . . . ", true);
  35. if (!Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
  36. {
  37. Directory.CreateDirectory(BUNDLE_MANAGER_OUTPUT_PATH);
  38. }
  39. PrebuildProjectSettingUpdate();
  40. if (String.IsNullOrEmpty(transitionScenePath))
  41. {
  42. // Get current editor script's directory as base path
  43. string[] res = System.IO.Directory.GetFiles(Application.dataPath, "OVRBundleManager.cs", SearchOption.AllDirectories);
  44. if (res.Length > 1)
  45. {
  46. OVRBundleTool.PrintError("More than one OVRBundleManager editor script has been found, please double check your Oculus SDK import.");
  47. return;
  48. }
  49. else
  50. {
  51. // Append Transition Scene's relative path to base path
  52. var OVREditorScript = Path.GetDirectoryName(res[0]);
  53. transitionScenePath = Path.Combine(OVREditorScript, TRANSITION_SCENE_RELATIVE_PATH);
  54. }
  55. }
  56. string[] buildScenes = new string[1] { transitionScenePath };
  57. string apkOutputPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "OVRTransition.apk");
  58. DateTime apkBuildStart = DateTime.Now;
  59. #if UNITY_2018_1_OR_NEWER
  60. BuildReport report = BuildPipeline.BuildPlayer(buildScenes, apkOutputPath, BuildTarget.Android,
  61. BuildOptions.Development | BuildOptions.AutoRunPlayer);
  62. if (report.summary.result == BuildResult.Succeeded)
  63. {
  64. OVRBundleTool.PrintSuccess();
  65. }
  66. else if (report.summary.result == BuildResult.Failed)
  67. {
  68. OVRBundleTool.PrintError();
  69. }
  70. #else
  71. string error = BuildPipeline.BuildPlayer(buildScenes, apkOutputPath, BuildTarget.Android,
  72. BuildOptions.Development | BuildOptions.AutoRunPlayer);
  73. if (string.IsNullOrEmpty(error))
  74. {
  75. OVRBundleTool.PrintSuccess();
  76. }
  77. else
  78. {
  79. OVRBundleTool.PrintError();
  80. }
  81. #endif
  82. OVRPlugin.SendEvent("oculus_bundle_tool", "apk_build_time", (DateTime.Now - apkBuildStart).TotalSeconds.ToString());
  83. PostbuildProjectSettingUpdate();
  84. }
  85. private static void PrebuildProjectSettingUpdate()
  86. {
  87. // Modify application identifier for transition APK
  88. projectDefaultAppIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
  89. PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,
  90. projectDefaultAppIdentifier + GetTransitionApkOptionalIdentifier());
  91. // Set VersionCode as a unique identifier for transition APK
  92. projectDefaultVersion = PlayerSettings.bundleVersion;
  93. PlayerSettings.bundleVersion = TRANSITION_APK_VERSION_NAME;
  94. // Modify IL2CPP option as it strips script symbols that are necessary for the scenes at runtime
  95. projectScriptImplementation = PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup);
  96. if (projectScriptImplementation != ScriptingImplementation.Mono2x)
  97. {
  98. // Show message in console to make it more clear to developers
  99. OVRBundleTool.PrintLog("Build will use Mono as scripting backend.");
  100. PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, ScriptingImplementation.Mono2x);
  101. }
  102. // Avoid stripping managed code that are necessary for the scenes at runtime
  103. #if UNITY_2018_3_OR_NEWER
  104. projectManagedStrippingLevel = PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android);
  105. if (projectManagedStrippingLevel != ManagedStrippingLevel.Disabled)
  106. {
  107. OVRBundleTool.PrintLog("Build will set Managed Stripping Level to Disabled.");
  108. PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, ManagedStrippingLevel.Disabled);
  109. }
  110. #else
  111. projectStrippingLevel = PlayerSettings.strippingLevel;
  112. if (projectStrippingLevel != StrippingLevel.Disabled)
  113. {
  114. OVRBundleTool.PrintLog("Build will set Stripping Level to Disabled.");
  115. PlayerSettings.strippingLevel = StrippingLevel.Disabled;
  116. }
  117. #endif
  118. projectStripEngineCode = PlayerSettings.stripEngineCode;
  119. if (projectStripEngineCode)
  120. {
  121. PlayerSettings.stripEngineCode = false;
  122. }
  123. }
  124. private static void PostbuildProjectSettingUpdate()
  125. {
  126. // Restore application identifier
  127. PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android,
  128. projectDefaultAppIdentifier);
  129. // Restore version setting
  130. PlayerSettings.bundleVersion = projectDefaultVersion;
  131. // Restore scripting backend option
  132. if (PlayerSettings.GetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup) != projectScriptImplementation)
  133. {
  134. PlayerSettings.SetScriptingBackend(EditorUserBuildSettings.selectedBuildTargetGroup, projectScriptImplementation);
  135. }
  136. // Restore managed stripping level
  137. #if UNITY_2018_3_OR_NEWER
  138. if (PlayerSettings.GetManagedStrippingLevel(BuildTargetGroup.Android) != projectManagedStrippingLevel)
  139. {
  140. PlayerSettings.SetManagedStrippingLevel(BuildTargetGroup.Android, projectManagedStrippingLevel);
  141. }
  142. #else
  143. if (PlayerSettings.strippingLevel != projectStrippingLevel)
  144. {
  145. PlayerSettings.strippingLevel = projectStrippingLevel;
  146. }
  147. #endif
  148. if (PlayerSettings.stripEngineCode != projectStripEngineCode)
  149. {
  150. PlayerSettings.stripEngineCode = projectStripEngineCode;
  151. }
  152. }
  153. // Build and deploy a list of scenes. It's suggested to only build and deploy one active scene that's being modified and
  154. // its dependencies such as scenes that are loaded additively
  155. public static void BuildDeployScenes(List<OVRBundleTool.EditorSceneInfo> sceneList, bool forceRestart)
  156. {
  157. externalSceneCache = EXTERNAL_STORAGE_PATH + "/" + PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
  158. + GetTransitionApkOptionalIdentifier() + "/cache/scenes";
  159. BuildSceneBundles(sceneList);
  160. if (DeploySceneBundles(sceneList))
  161. {
  162. if (forceRestart)
  163. {
  164. LaunchApplication();
  165. return;
  166. }
  167. OVRBundleTool.PrintSuccess();
  168. return;
  169. }
  170. }
  171. // Build assets that are used by scenes from the given list.
  172. // This function will automatically de-duplicated same asset file being referenced in multiple scenes.
  173. // Scene bundles will be created with name pattern: <SceneName>_<AssetTypeExtention><ChunckNumber>
  174. private static void BuildSceneBundles(List<OVRBundleTool.EditorSceneInfo> sceneList)
  175. {
  176. DateTime totalStart = DateTime.Now;
  177. // Keeps track of dependent assets across scenes
  178. // to ensure each asset is only packaged once in one of the scene bundles.
  179. // uniqueAssetInSceneBundle is a map from "asset unique identifier" to the first scene that references the asset.
  180. // It supports different assets with same file name as "asset unique identifier" contain full qualified asset file path
  181. Dictionary<string, string> uniqueAssetInSceneBundle = new Dictionary<string, string>();
  182. // List of bundle targets for Unity's build pipeline to package
  183. List<AssetBundleBuild> assetBundleBuilds = new List<AssetBundleBuild>();
  184. // Map that contains "asset type" to list of assets that are of the asset type
  185. Dictionary<string, List<string>> extToAssetList = new Dictionary<string, List<string>>();
  186. // Check if assets in Resources need to be packaged
  187. if (CheckForResources())
  188. {
  189. var resourceDirectories = Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories).ToArray();
  190. // Fetch a list of all files in resource directory
  191. string[] resourceAssetPaths = AssetDatabase.FindAssets("", resourceDirectories).Select(x => AssetDatabase.GUIDToAssetPath(x)).ToArray();
  192. ProcessAssets(resourceAssetPaths, "resources", ref uniqueAssetInSceneBundle, ref extToAssetList);
  193. AssetBundleBuild resourceBundle = new AssetBundleBuild();
  194. resourceBundle.assetNames = uniqueAssetInSceneBundle.Keys.ToArray();
  195. resourceBundle.assetBundleName = OVRSceneLoader.resourceBundleName;
  196. assetBundleBuilds.Add(resourceBundle);
  197. }
  198. // Create scene bundle output directory
  199. string sceneBundleDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
  200. if (!Directory.Exists(sceneBundleDirectory))
  201. {
  202. Directory.CreateDirectory(sceneBundleDirectory);
  203. }
  204. OVRBundleTool.PrintLog("Building scene bundles . . . ");
  205. DateTime labelingStart = DateTime.Now;
  206. foreach (var scene in sceneList)
  207. {
  208. // Get all the assets that the scene depends on and sort them by type
  209. DateTime getDepStart = DateTime.Now;
  210. string[] assetDependencies = AssetDatabase.GetDependencies(scene.scenePath);
  211. Debug.Log("[OVRBundleManager] - " + scene.sceneName + " - Calculated scene asset dependencies in: " + (DateTime.Now - getDepStart).TotalSeconds);
  212. ProcessAssets(assetDependencies, scene.sceneName, ref uniqueAssetInSceneBundle, ref extToAssetList);
  213. // Add the scene into it's own bundle
  214. string[] sceneAsset = new string[1] { scene.scenePath };
  215. AssetBundleBuild sceneBuild = new AssetBundleBuild();
  216. sceneBuild.assetBundleName = "scene_" + scene.sceneName;
  217. sceneBuild.assetNames = sceneAsset;
  218. assetBundleBuilds.Add(sceneBuild);
  219. }
  220. // Chunk the asset bundles by number of assets
  221. foreach (string ext in extToAssetList.Keys)
  222. {
  223. int assetCount = extToAssetList[ext].Count;
  224. int numChunks = (assetCount + BUNDLE_CHUNK_SIZE - 1) / BUNDLE_CHUNK_SIZE;
  225. //Debug.Log(ext + " has " + assetCount + " asset(s) that will be split into " + numChunks + " chunk(s)");
  226. for (int i = 0; i < numChunks; i++)
  227. {
  228. List<string> assetChunkList;
  229. if (i == numChunks - 1)
  230. {
  231. int size = BUNDLE_CHUNK_SIZE - (numChunks * BUNDLE_CHUNK_SIZE - assetCount);
  232. assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, size);
  233. }
  234. else
  235. {
  236. assetChunkList = extToAssetList[ext].GetRange(i * BUNDLE_CHUNK_SIZE, BUNDLE_CHUNK_SIZE);
  237. }
  238. AssetBundleBuild build = new AssetBundleBuild();
  239. build.assetBundleName = "asset_" + ext + i;
  240. build.assetNames = assetChunkList.ToArray();
  241. //Debug.Log("Chunk " + i + " has " + assetChunkList.Count + " asset(s)");
  242. assetBundleBuilds.Add(build);
  243. }
  244. }
  245. Debug.Log("[OVRBundleManager] - Created chucked scene bundles in: " + (DateTime.Now - labelingStart).TotalSeconds);
  246. // Build asset bundles
  247. BuildPipeline.BuildAssetBundles(sceneBundleDirectory, assetBundleBuilds.ToArray(),
  248. BuildAssetBundleOptions.UncompressedAssetBundle, BuildTarget.Android);
  249. double bundleBuildTime = (DateTime.Now - totalStart).TotalSeconds;
  250. Debug.Log("[OVRBundleManager] - Total Time: " + bundleBuildTime);
  251. OVRPlugin.SendEvent("oculus_bundle_tool", "bundle_build_time", bundleBuildTime.ToString());
  252. }
  253. private static void ProcessAssets(string[] assetPaths,
  254. string assetParent,
  255. ref Dictionary<string, string> uniqueAssetInSceneBundle,
  256. ref Dictionary<string, List<string>> extToAssetList)
  257. {
  258. foreach (string asset in assetPaths)
  259. {
  260. string ext = Path.GetExtension(asset);
  261. if (!string.IsNullOrEmpty(ext))
  262. {
  263. ext = ext.Substring(1);
  264. if (!ext.Equals("cs") && !ext.Equals("unity"))
  265. {
  266. // Only process each asset once across all resource and scene bundles
  267. // Each asset is keyed by full path as a unique identifier
  268. if (!uniqueAssetInSceneBundle.ContainsKey(asset))
  269. {
  270. var assetObject = (UnityEngine.Object)AssetDatabase.LoadAssetAtPath(asset, typeof(UnityEngine.Object));
  271. if (assetObject == null || (assetObject.hideFlags & HideFlags.DontSaveInBuild) == 0)
  272. {
  273. uniqueAssetInSceneBundle[asset] = assetParent;
  274. if (assetParent != "resources")
  275. {
  276. if (!extToAssetList.ContainsKey(ext))
  277. {
  278. extToAssetList[ext] = new List<string>();
  279. }
  280. extToAssetList[ext].Add(asset);
  281. }
  282. }
  283. }
  284. }
  285. }
  286. }
  287. }
  288. private static bool DeploySceneBundles(List<OVRBundleTool.EditorSceneInfo> sceneList)
  289. {
  290. // Create Temp directory on local machine if it does not exist
  291. string tempDirectory = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, "Temp");
  292. if (!Directory.Exists(tempDirectory))
  293. {
  294. Directory.CreateDirectory(tempDirectory);
  295. }
  296. string absoluteTempPath = Path.Combine(Path.Combine(Application.dataPath, ".."), tempDirectory);
  297. OVRBundleTool.PrintLog("Deploying scene bundles to device . . . ");
  298. OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
  299. if (adbTool.isReady)
  300. {
  301. DateTime transferStart = DateTime.Now;
  302. OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.TRANSFERRING);
  303. // Transfer all scene bundles that are relavent
  304. if (!TransferSceneBundles(adbTool, absoluteTempPath, externalSceneCache))
  305. {
  306. return false;
  307. }
  308. OVRBundleTool.UpdateSceneBuildStatus(OVRBundleTool.SceneBundleStatus.DEPLOYED);
  309. // Create file to tell transition scene APK which scene to load and push it to the device
  310. string sceneLoadDataPath = Path.Combine(tempDirectory, OVRSceneLoader.sceneLoadDataName);
  311. if (File.Exists(sceneLoadDataPath))
  312. {
  313. File.Delete(sceneLoadDataPath);
  314. }
  315. StreamWriter writer = new StreamWriter(sceneLoadDataPath, true);
  316. // Write version and scene names
  317. long unixTime = (int)(DateTimeOffset.UtcNow.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
  318. writer.WriteLine(unixTime.ToString());
  319. for (int i = 0; i < sceneList.Count; i++)
  320. {
  321. writer.WriteLine(Path.GetFileNameWithoutExtension(sceneList[i].scenePath));
  322. }
  323. writer.Close();
  324. string absoluteSceneLoadDataPath = Path.Combine(absoluteTempPath, OVRSceneLoader.sceneLoadDataName);
  325. string[] pushCommand = { "-d push", "\"" + absoluteSceneLoadDataPath + "\"", "\"" + externalSceneCache + "\"" };
  326. string output, error;
  327. if (adbTool.RunCommand(pushCommand, null, out output, out error) == 0)
  328. {
  329. Debug.Log("[OVRBundleManager] Scene Load Data Pushed to Device.");
  330. OVRPlugin.SendEvent("oculus_bundle_tool", "transfer_bundle_time", (DateTime.Now - transferStart).TotalSeconds.ToString());
  331. return true;
  332. }
  333. OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
  334. }
  335. else
  336. {
  337. OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
  338. }
  339. return false;
  340. }
  341. private static bool TransferSceneBundles(OVRADBTool adbTool, string absoluteTempPath, string externalSceneCache)
  342. {
  343. List<string> bundlesToTransfer = new List<string>();
  344. List<string> bundlesToDelete = new List<string>();
  345. string manifestFilePath = externalSceneCache + "/" + BUNDLE_MANAGER_MASTER_BUNDLE;
  346. string[] pullManifestCommand = { "-d pull", "\"" + manifestFilePath + "\"", "\"" + absoluteTempPath + "\"" };
  347. string output, error;
  348. if (adbTool.RunCommand(pullManifestCommand, null, out output, out error) == 0)
  349. {
  350. // An existing manifest file was found on device. Load hashes and upload bundles that have changed hashes.
  351. Debug.Log("[OVRBundleManager] - Scene bundle manifest file found. Decoding changes . . .");
  352. // Load hashes from remote manifest
  353. AssetBundle remoteBundle = AssetBundle.LoadFromFile(Path.Combine(absoluteTempPath, BUNDLE_MANAGER_MASTER_BUNDLE));
  354. if (remoteBundle == null)
  355. {
  356. OVRBundleTool.PrintError("Failed to load remote asset bundle manifest file.");
  357. return false;
  358. }
  359. AssetBundleManifest remoteManifest = remoteBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  360. Dictionary<string, Hash128> remoteBundleToHash = new Dictionary<string, Hash128>();
  361. if (remoteManifest != null)
  362. {
  363. string[] assetBundles = remoteManifest.GetAllAssetBundles();
  364. foreach (string bundleName in assetBundles)
  365. {
  366. remoteBundleToHash[bundleName] = remoteManifest.GetAssetBundleHash(bundleName);
  367. }
  368. }
  369. remoteBundle.Unload(true);
  370. // Load hashes from local manifest
  371. AssetBundle localBundle = AssetBundle.LoadFromFile(BUNDLE_MANAGER_OUTPUT_PATH + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE
  372. + "\\" + BUNDLE_MANAGER_MASTER_BUNDLE);
  373. if (localBundle == null)
  374. {
  375. OVRBundleTool.PrintError("<color=red>Failed to load local asset bundle manifest file.\n</color>");
  376. return false;
  377. }
  378. AssetBundleManifest localManifest = localBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
  379. if (localManifest != null)
  380. {
  381. Hash128 zeroHash = new Hash128(0, 0, 0, 0);
  382. // Build a list of dirty bundles that will have to be transfered
  383. string relativeSceneBundlesPath = Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE);
  384. bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, BUNDLE_MANAGER_MASTER_BUNDLE));
  385. string[] assetBundles = localManifest.GetAllAssetBundles();
  386. foreach (string bundleName in assetBundles)
  387. {
  388. if (!remoteBundleToHash.ContainsKey(bundleName))
  389. {
  390. bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
  391. }
  392. else
  393. {
  394. if (remoteBundleToHash[bundleName] != localManifest.GetAssetBundleHash(bundleName))
  395. {
  396. bundlesToTransfer.Add(Path.Combine(relativeSceneBundlesPath, bundleName));
  397. }
  398. remoteBundleToHash[bundleName] = zeroHash;
  399. }
  400. }
  401. OVRBundleTool.PrintLog(bundlesToTransfer.Count + " dirty bundle(s) will be transfered.\n");
  402. }
  403. }
  404. else
  405. {
  406. if (output.Contains("does not exist"))
  407. {
  408. // Fresh install of asset bundles, transfer all asset bundles
  409. OVRBundleTool.PrintLog("Manifest file not found. Transfering all bundles . . . ");
  410. string[] mkdirCommand = { "-d shell", "mkdir -p", "\"" + externalSceneCache + "\"" };
  411. if (adbTool.RunCommand(mkdirCommand, null, out output, out error) == 0)
  412. {
  413. string absoluteSceneBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."),
  414. Path.Combine(BUNDLE_MANAGER_OUTPUT_PATH, BUNDLE_MANAGER_MASTER_BUNDLE));
  415. string[] assetBundlePaths = Directory.GetFiles(absoluteSceneBundlePath);
  416. if (assetBundlePaths.Length != 0)
  417. {
  418. foreach (string path in assetBundlePaths)
  419. {
  420. if (!path.Contains(".manifest"))
  421. {
  422. bundlesToTransfer.Add(path);
  423. }
  424. }
  425. }
  426. else
  427. {
  428. OVRBundleTool.PrintError("Failed to locate scene bundles to transfer.");
  429. return false;
  430. }
  431. }
  432. }
  433. }
  434. // If any adb error occured during manifest calculation, print it and return false
  435. if (!string.IsNullOrEmpty(error) || output.Contains("error"))
  436. {
  437. OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
  438. return false;
  439. }
  440. // Transfer bundles to device
  441. DateTime transferStart = DateTime.Now;
  442. foreach (string bundle in bundlesToTransfer)
  443. {
  444. string absoluteBundlePath = Path.Combine(Path.Combine(Application.dataPath, ".."), bundle);
  445. string[] pushBundleCommand = { "-d push", "\"" + absoluteBundlePath + "\"", "\"" + externalSceneCache + "\"" };
  446. adbTool.RunCommandAsync(pushBundleCommand, null);
  447. }
  448. Debug.Log("[OVRBundleManager] - Transfer took " + (DateTime.Now - transferStart).TotalSeconds + " seconds.");
  449. // Delete stale bundles on device
  450. if (bundlesToDelete.Count > 0)
  451. {
  452. foreach (string bundle in bundlesToDelete)
  453. {
  454. string bundlePath = externalSceneCache + "/" + bundle;
  455. string[] deleteBundleCommand = { "-d shell", "rm", "\"" + bundlePath + "\"" };
  456. adbTool.RunCommandAsync(deleteBundleCommand, null);
  457. }
  458. OVRBundleTool.PrintLog("Deleted " + bundlesToDelete.Count + " bundle(s) that were stale");
  459. }
  460. return true;
  461. }
  462. public static bool LaunchApplication()
  463. {
  464. OVRBundleTool.PrintLog("Launching Application . . . ");
  465. OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
  466. if (adbTool.isReady)
  467. {
  468. string output, error;
  469. string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
  470. + GetTransitionApkOptionalIdentifier();
  471. string playerActivityName = "\"" + appPackagename + "/com.unity3d.player.UnityPlayerActivity\"";
  472. string[] appStartCommand = { "-d shell", "am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -S -f 0x10200000 -n", playerActivityName };
  473. if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
  474. {
  475. OVRBundleTool.PrintSuccess();
  476. OVRBundleTool.PrintLog("App package " + appPackagename + " is launched.");
  477. return true;
  478. }
  479. string completeError = "Failed to launch application. Try launching it manually through the device.\n" + (string.IsNullOrEmpty(error) ? output : error);
  480. OVRBundleTool.PrintError(completeError);
  481. }
  482. else
  483. {
  484. OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
  485. }
  486. return false;
  487. }
  488. public static bool UninstallAPK()
  489. {
  490. OVRBundleTool.PrintLog("Uninstalling Application . . .");
  491. OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
  492. if (adbTool.isReady)
  493. {
  494. string output, error;
  495. string appPackagename = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android)
  496. + GetTransitionApkOptionalIdentifier();
  497. string[] appStartCommand = { "-d shell", "pm uninstall", appPackagename };
  498. if (adbTool.RunCommand(appStartCommand, null, out output, out error) == 0)
  499. {
  500. OVRBundleTool.PrintSuccess();
  501. OVRBundleTool.PrintLog("App package " + appPackagename + " is uninstalled.");
  502. return true;
  503. }
  504. OVRBundleTool.PrintError("Failed to uninstall APK.");
  505. }
  506. else
  507. {
  508. OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
  509. }
  510. return false;
  511. }
  512. public static void DeleteRemoteAssetBundles()
  513. {
  514. OVRADBTool adbTool = new OVRADBTool(OVRConfig.Instance.GetAndroidSDKPath());
  515. if (adbTool.isReady)
  516. {
  517. bool failure = false;
  518. string fileExistsError = "No such file or directory";
  519. OVRBundleTool.PrintLog("Deleting device bundles . . . ");
  520. string output, error;
  521. string[] deleteBundleCommand = { "-d shell", "rm -r", externalSceneCache };
  522. if (adbTool.RunCommand(deleteBundleCommand, null, out output, out error) != 0)
  523. {
  524. if (!(output.Contains(fileExistsError) || error.Contains(fileExistsError)))
  525. {
  526. failure = true;
  527. }
  528. }
  529. if (failure)
  530. {
  531. OVRBundleTool.PrintError(string.IsNullOrEmpty(error) ? output : error);
  532. OVRBundleTool.PrintError("Failed to delete scene bundle cache directory.");
  533. }
  534. else
  535. {
  536. OVRBundleTool.PrintSuccess();
  537. }
  538. }
  539. else
  540. {
  541. OVRBundleTool.PrintError(ADB_TOOL_INITIALIZE_ERROR);
  542. }
  543. }
  544. public static void DeleteLocalAssetBundles()
  545. {
  546. try
  547. {
  548. if (Directory.Exists(BUNDLE_MANAGER_OUTPUT_PATH))
  549. {
  550. OVRBundleTool.PrintLog("Deleting local bundles . . . ");
  551. Directory.Delete(BUNDLE_MANAGER_OUTPUT_PATH, true);
  552. }
  553. }
  554. catch (Exception e)
  555. {
  556. OVRBundleTool.PrintError(e.Message);
  557. }
  558. OVRBundleTool.PrintSuccess();
  559. }
  560. private static bool CheckForResources()
  561. {
  562. string[] resourceDirectories = Directory.GetDirectories("Assets", "Resources", SearchOption.AllDirectories);
  563. return resourceDirectories.Length > 0;
  564. }
  565. private static string GetTransitionApkOptionalIdentifier()
  566. {
  567. string transitionApkOptionalIdentifier;
  568. // Check option value from editor UI
  569. if (OVRBundleTool.GetUseOptionalTransitionApkPackage())
  570. {
  571. // Append .transition to default app package name to optionally allow both
  572. // full build apk and transition apk to be installed on device
  573. transitionApkOptionalIdentifier = ".transition";
  574. }
  575. else
  576. {
  577. transitionApkOptionalIdentifier = "";
  578. }
  579. return transitionApkOptionalIdentifier;
  580. }
  581. }
  582. #endif