Why does Leak Canary detect Google firebase APIs as Leaks? - firebase

Why does Leak Canary detect Google firebase APIs as Leaks?
When I launch an app while using firebase, it keeps on detecting memory leaks. Is there a way to stop this? Thanks.
┬───
│ GC Root: Global variable in native code
│
├─ com.google.firebase.auth.api.fallback.service.zza instance
│ Leaking: UNKNOWN
│ Retaining 634 B in 9 objects
│ zza instance of com.google.firebase.auth.api.fallback.service.
│ FirebaseAuthFallbackService
│ ↓ zza.zza
│ ~~~
╰→ com.google.firebase.auth.api.fallback.service.FirebaseAuthFallbackService
​ instance
​ Leaking: YES (ObjectWatcher was watching this because com.google.firebase.
​ auth.api.fallback.service.FirebaseAuthFallbackService received
​ Service#onDestroy() callback)
​ Retaining 606 B in 8 objects
​ key = 7ab80331-3185-47d4-b999-118434212080
​ watchDurationMillis = 5284
​ retainedDurationMillis = 278
​ mApplication instance of com.example.app.App
​ mBase instance of android.app.ContextImpl
METADATA
Build.VERSION.SDK_INT: 27
Build.MANUFACTURER: iLA
LeakCanary version: 2.6
App process name: com.example.app
Stats: LruCache[maxSize=3000,hits=1327,misses=42351,hitRate=3%]
RandomAccess[bytes=2171000,reads=42351,travel=15377633875,range=14122402,size=17
486349]
Heap dump reason: 1 retained objects, app is not visible
Analysis duration: 16095 ms```

https://github.com/firebase/firebase-android-sdk/issues/2387
Edited:
Here is the suggested workaround to ignore that small leak from the site:
First, disable the automatic install:
<resources>
<bool name="leak_canary_watcher_auto_install">false</bool>
</resources>
Then install LeakCanary manually in your debug application, ignoring FirebaseAuthFallbackService:
class DebugExampleApplication : ExampleApplication() {
override fun onCreate() {
super.onCreate()
val delegate = ReachabilityWatcher { watchedObject, description ->
if (watchedObject::class.java.name != "com.google.firebase.auth.api.fallback.service.FirebaseAuthFallbackService
​ instance") {
AppWatcher.objectWatcher.expectWeaklyReachable(watchedObject, description)
}
}
val watchersToInstall = AppWatcher.appDefaultWatchers(application, delegate)
AppWatcher.manualInstall(
application = application,
watchersToInstall = watchersToInstall
)
}
}

Related

Failed to parse private key: Error: Invalid PEM formatted message

I am trying to deploy a NEXT JS app that uses firebase-admin on Vercel.
import * as firebaseAdmin from 'firebase-admin';
import firebase from 'firebase/app';
if (!firebaseAdmin.apps.length) {
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert({
privateKey: process.env.NEXT_PUBLIC_FIREBASE_PRIVATE_KEY,
clientEmail: process.env.NEXT_PUBLIC_FIREBASE_CLIENT_EMAIL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
}),
databaseURL: process.env.NEXT_PUBLIC_FIREBASE_DATABASE_URL,
});
}
When I deploy it to vercel with environment variables as:
NEXT_PUBLIC_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n....\n-----END PRIVATE KEY-----\n"
I get this error message.
> Build error occurred
Error: Certificate object must contain a string "private_key" property.
at FirebaseAppError.FirebaseError [as constructor] (/vercel/path0/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (/vercel/path0/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseAppError (/vercel/path0/node_modules/firebase-admin/lib/utils/error.js:122:28)
at new Certificate (/vercel/path0/node_modules/firebase-admin/lib/auth/credential.js:118:19)
at new CertCredential (/vercel/path0/node_modules/firebase-admin/lib/auth/credential.js:187:64)
at Object.cert (/vercel/path0/node_modules/firebase-admin/lib/firebase-namespace.js:220:58)
at Object.3996 (/vercel/path0/.next/server/chunks/241.js:115:76)
at __webpack_require__ (/vercel/path0/.next/server/webpack-runtime.js:25:42)
at /vercel/path0/.next/server/pages/dashboard.js:124:81
at Function.__webpack_require__.a (/vercel/path0/.next/server/webpack-runtime.js:103:13) {
type: 'FirebaseAppError',
errorInfo: {
code: 'app/invalid-credential',
message: 'Certificate object must contain a string "private_key" property.'
},
codePrefix: 'app'
}
Error! Command "npm run build" exited with 1
Error: Command "vercel build" exited with 1
However running npm run build builds with no error locally.
.env.local
info - Linting and checking validity of types
info - Creating an optimized production build
info - Compiled successfully
info - Collecting page data
info - Generating static pages (4/4)
info - Finalizing page optimization
Page Size First Load JS
┌ ○ / (466 ms) 461 B 271 kB
├ /_app 0 B 270 kB
├ ○ /404 (450 ms) 526 B 271 kB
├ λ /api/hello 0 B 270 kB
+ First Load JS shared by all 270 kB
├ chunks/framework-a87821de553db91d.js 45 kB
├ chunks/main-567f0ec5ceee81fc.js 29.7 kB
├ chunks/pages/_app-b443b414c9e8f379.js 195 kB
├ chunks/webpack-42cdea76c8170223.js 1.07 kB
└ css/966e875c066cf46b.css 5.18 kB
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
I do not understand how to fix this because this runs locally and builds without any errors.
I found the answer.
.env.local can have values like
NEXT_PUBLIC_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n....\n-----END PRIVATE KEY-----\n"
But in vercel environment variables add the keys without the double quotes "".
NEXT_PUBLIC_FIREBASE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n....\n-----END PRIVATE KEY-----\n
When you add the keys it should reformat itself and look something like this.
You can console.log the values locally, that will look like this below and paste the exact same value in Vercel env.
-----BEGIN PRIVATE KEY-----
your
secret
key
-----END PRIVATE KEY-----
I ran into this problem too. Removing the quotes didn't help this far.
However, my comment is related to a different thing: You are setting the private key to a NEXT_PUBLIC var (accessible via front-end, hence, exposed).
Just that. Sorry if it's not relevant.

Facing error with terraform code for importing multiple runbook to azure automation account

I have created the azure automation account using terraform code. I have multiple runbooks PowerShell scripts saved in my local. I am using for.each option to import all the runbooks at a time. But I am getting some errors while running the terraform file. Please find my code below:
resource "azurerm_automation_runbook" "example" {
for_each = fileset(".", "./Azure_Runbooks/*.ps1")
name = ["${split("/", each.value)}"][1]
location = var.location
resource_group_name = var.resource_group
automation_account_name = azurerm_automation_account.example.name
log_verbose = var.log_verbose
log_progress = var.log_progress
runbook_type = var.runbooktype
content = filemd5("${each.value}")
}
Error:
Error: Invalid index
│
│ on AutomationAccount\main.tf line 51, in resource "azurerm_automation_runbook" "example":
│ 51: name = ["${split("/", each.value)}"][1]
│ ├────────────────
│ │ each.value will be known only after apply
│
│ The given key does not identify an element in this collection value: the given index is greater than
│ or equal to the length of the collection.
Can someone please help how I can upload all my existing runbook scrips to the newly created automation account using terraform code.
You don't need list in a list. So instead of
name = ["${split("/", each.value)}"][1]
it should be
name = split("/", each.value)[1]

Javascript bridge / upcall to JavaFX (via JSObject.setMember() method) breaks when moving class into a package

This is similar to this question, but I believe we are encountering different issues.
Setup:
I have a Kotlin class that interfaces with a TinyMCE instance running in a JavaFX Webview. I am setting up upcalls from Javascript to JavaFX as shown below, in the TinyMCEInterface class:
inner class BridgeObject {
fun setEditorAndSelection(ed : JSObject?) {
editorObj = ed
selectionObj = editorObj?.getMember("selection") as JSObject?
}
fun setInterfaceContent(newContent : String) {
contentProp.value = newContent
}
}
// after WebEngine loads successfully
val bridgeObj : BridgeObject = BridgeObject()
(webEngine.executeScript("window") as JSObject).setMember("bridgeObj", bridgeObj)
I am then calling these methods from Javascript, in TinyMCE setup:
ed.on('init', function() {
ed.setContent(initContent)
window.bridgeObj.setEditorAndSelection(ed)
window.bridgeObj.setInterfaceContent(ed.getContent())
});
ed.on('Change SelectionChange', function(e) {
window.bridgeObj.setInterfaceContent(ed.getContent())
});
Problem:
This works perfectly fine when the TinyMCEInterface class file lies in the root directory of my application (in package com.myself.app). That is, when the file structure looks like this:
com.myself.app/
│
├─ TinyMCEInterface.kt
│
├─ Main.kt
But breaks when I move TinyMCEInterface into a package (in package com.myself.app.somepackage):
com.myself.app/
│
├─ somepackage/
│ ├─ TinyMCEInterface.kt
│
├─ Main.kt
When I say "breaks", there are no errors; the calls to member functions of window.bridgeObj simply do not happen and quietly fail. I am completely bewildered as to how this can be happening.
Thanks in advance for any advice!

Fragment leaking in ViewPager2 with FragmentStateAdapter on onDestroyView()

FYI: The question is short, but just in case I added more information at the end that may be relevant.
I needed an infinite scrolling ViewPager2, and I wanted to reuse a Fragment from the project as it was already designed and calls already well stablished with its viewLifeCycle..
Also I am aware the VP recycles offscreen Fragments (1 offset position from Fragment shown) and has at least up to 3 Fragments at any given moment, so using Fragments was the choice.
The problem is that when going to the fourth page, the ViewPager2 tries to remove the first Fragment (as expected) and LeakCanary shows me this (Entire diagnosis at the end.):
D/LeakCanary: Watching instance of androidx.core.widget.NestedScrollView (com.****.****.ui.***.pages.add_element.SearchPageFragment2 received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) with key 294cd9eb-3d6a-4c98-a69c-5d20e4c1652f
The diagnosis never points to my references, only to android library references.
Before the code bellow I had more lines, But I've been trimming them until kept with the bare minimum and the leak is still there.
// ----- onViewCreated() ------
MyPagerAdapter mPa = new MyPagerAdapter(
getChildFragmentManager(),
getViewLifecycleOwner().getLifecycle()
);
vp.setAdapter(mPa);
MyPagerAdapter.class that extends FragmentStateAdapter:
#NonNull
#Override
public Fragment createFragment(int position) {
return new SearchPageFragment2(); //Test Fragment
}
#Override
public int getItemCount() {
return 8; //Test fixed number
}
The Leaking Fragment:
public class SearchPageFragment2 extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return FragmentSearchPageBinding.inflate(inflater).getRoot();
}
}
What is causing the memory leak??
The question ENDS HERE.
Preamble...
The main View (the farthest Fragment ancestor at display while the leak is happening) is a BackStackEntry that we navigate to, from the Home Fragment, this View holds a toolBar with main info about the app, bellow the toolBar is the main content of this view, a ViewPager2 of fixed size with 3 Fragments, and on the first Fragment... a "MutableFrameLayout" that I created:
private final MutableFrameLayoutAdapter<ElementDBLoaderViewModel.FrameActions> adapter = new MutableFrameLayoutAdapter<>(
this, //Fragment owner
this::getChildFragmentManager, //FragmentManager supplier
() -> ElementDBLoaderViewModel.FrameActions.initiating, //initialValue
action -> { //Function<X, Fragment>
switch (action) {
case crossed:
return new AddElementExpandedFragment();
case not_crossed:
return new AddElementFragment();
case explore:
return new MainDBPaginationFragment2();
}
return null;
}
);
So that it can be used like this:
binding.fragmentContainer.setAdapter(adapter);
adapter.changeContent(ElementDBLoaderViewModel.FrameActions.crossed)
The component is leak proof, with hours of testing on different situations.
The main "engine" of this component:
......
if (oldFragment != null) {
FragmentTransaction ft = stackFm.beginTransaction();
ft.remove(oldFragment);
addCommit(ft, newFragment);
}
Where "stackFm" is the result of having acquired the FragmentManager supplier in the constructor with childFragmentManager.get() ("this::getChildFragmentManager")
......
private void addCommit(FragmentTransaction ft, Fragment newFragment) {
fragmentCreated.get().fragmentCreated(newFragment); //stateless adapter interface reference
ft.add(getId(), newFragment);
ft.commit();
}
......
The idea was to have a component easy to use with nothing fancy and straight forward.
Basically the first page (Fragment) of the ViewPager2 of fixed size where this MutableFrameLayout is placed, can take the form of 3 different Fragments (depending on DB size).
The Leaking ViewPager2 is inside MainDBPaginationFragment2.class, BUT BEFORE arriving to the MainDBPaginationFragment2 Fragment, we MUST first go through the AddElementExpandedFragment.class.
LEAK DIAGNOSIS (none of the references are mine)
┬───
│ GC Root: System class
│
├─ android.view.WindowManagerGlobal class
│ Leaking: NO (DecorView↓ is not leaking and a class is never leaking)
│ ↓ static WindowManagerGlobal.sDefaultWindowManager
├─ android.view.WindowManagerGlobal instance
│ Leaking: NO (DecorView↓ is not leaking)
│ ↓ WindowManagerGlobal.mViews
├─ java.util.ArrayList instance
│ Leaking: NO (DecorView↓ is not leaking)
│ ↓ ArrayList.elementData
├─ java.lang.Object[] array
│ Leaking: NO (DecorView↓ is not leaking)
│ ↓ Object[].[0]
├─ com.android.internal.policy.DecorView instance
│ Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking and View attached)
│ View is part of a window view hierarchy
│ View.mAttachInfo is not null (view attached)
│ View.mWindowAttachCount = 1
│ mContext instance of com.android.internal.policy.DecorContext, wrapping
│ activity com.****.****.ui.MainActivity with mDestroyed = false
│ ↓ DecorView.mAttachInfo
├─ android.view.View$AttachInfo instance
│ Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking)
│ ↓ View$AttachInfo.mScrollContainers
├─ java.util.ArrayList instance
│ Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking)
│ ↓ ArrayList.elementData
├─ java.lang.Object[] array
│ Leaking: NO (ViewPager2$RecyclerViewImpl↓ is not leaking)
│ ↓ Object[].[2]
├─ androidx.viewpager2.widget.ViewPager2$RecyclerViewImpl instance
│ Leaking: NO (View attached)
│ View is part of a window view hierarchy
│ View.mAttachInfo is not null (view attached)
│ View.mID = R.id.null
│ View.mWindowAttachCount = 1
│ mContext instance of com.****.****.ui.MainActivity with
│ mDestroyed = false
│ ↓ ViewPager2$RecyclerViewImpl.mRecycler
│ ~~~
├─ androidx.recyclerview.widget.RecyclerView$Recycler instance
│ Leaking: UNKNOWN
│ Retaining 48453 bytes in 442 objects
│ ↓ RecyclerView$Recycler.mRecyclerPool
│ ~~~~~
├─ androidx.recyclerview.widget.RecyclerView$RecycledViewPool instance
│ Leaking: UNKNOWN
│ Retaining 46192 bytes in 424 objects
│ ↓ RecyclerView$RecycledViewPool.mScrap
│ ~~
├─ android.util.SparseArray instance
│ Leaking: UNKNOWN
│ Retaining 46176 bytes in 423 objects
│ ↓ SparseArray.mValues
│ ~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 46111 bytes in 421 objects
│ ↓ Object[].[0]
│ ~
├─ androidx.recyclerview.widget.RecyclerView$RecycledViewPool$ScrapData instance
│ Leaking: UNKNOWN
│ Retaining 46067 bytes in 420 objects
│ ↓ RecyclerView$RecycledViewPool$ScrapData.mScrapHeap
│ ~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ Retaining 46035 bytes in 419 objects
│ ↓ ArrayList.elementData
│ ~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 46015 bytes in 418 objects
│ ↓ Object[].[0]
│ ~
├─ androidx.viewpager2.adapter.FragmentViewHolder instance
│ Leaking: UNKNOWN
│ Retaining 43762 bytes in 400 objects
│ ↓ FragmentViewHolder.itemView
│ ~~~~
├─ android.widget.FrameLayout instance
│ Leaking: UNKNOWN
│ Retaining 43677 bytes in 399 objects
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.null
│ View.mWindowAttachCount = 1
│ mContext instance of com.****.****.ui.MainActivity with
│ mDestroyed = false
│ ↓ FrameLayout.mMatchParentChildren
│ ~~~~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ Retaining 41573 bytes in 385 objects
│ ↓ ArrayList.elementData
│ ~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 41553 bytes in 384 objects
│ ↓ Object[].[0]
│ ~
╰→ androidx.core.widget.NestedScrollView instance
​ Leaking: YES (ObjectWatcher was watching this because com.****.
​ ****.ui.****.pages.add_element.SearchPageFragment2
​ received Fragment#onDestroyView() callback (references to its views
​ should be cleared to prevent leaks))
​ Retaining 41549 bytes in 383 objects
​ key = 294cd9eb-3d6a-4c98-a69c-5d20e4c1652f
​ watchDurationMillis = 7979
​ retainedDurationMillis = 2978
​ View not part of a window view hierarchy
​ View.mAttachInfo is null (view detached)
​ View.mID = R.id.scrolling_content_table
​ View.mWindowAttachCount = 1
​ mContext instance of com.****.****.ui.MainActivity with
​ mDestroyed = false
The solution for me at least was stop using the FragmentStateAdapter, and instead use a RecyclerViewAdapter, and submit to this adapter, a list of integers representing the index of each page (needed for db live connection), while at the same time determining the size of the batch of pages (given by the initial DB request).
Finally, on the view creation of each viewHolder, the content of each page was fed including resolved viewmodel contents.
A vital AND hardest part of the solution was granting each viewHolder its own lifeCycle, so that each could manage its db connection on its own.
As you can guess the code is horrendous, and even if I tried obsessively to order it the best I could, it is still not sufficiently organized IMO.
My interpretation of this is that doing pagination on your own is absolutely nightmarish... but possible.
Also encapsulating pagination functions such as page controls, db requests and display, make it so that the entire "concept" of pagination itself becomes a single unit on its own, making it absolutely difficult to separate each component on its own capsule as the function of every component is deeply interconnected with its consumer.
One example is the way your DB queries assume "pivot points" (whether they are inclusive or not, or maybe they are not even required at all and you can randomly jump at any page(index) you want), I am not sufficiently familiar with other db but if this changes enough across services, one may even need to change how the entire pagination works, bottom - up.
The components that could definitely be encapsulated are: the adapter, the "scroller" of your data, that will define the number of pages and page position while defining page control functions AND a viewmodel that will simply serve as an envelope to the scroller itself, but that's it. the actual display needs to be interwoven with all these components in the most savage way possible. and your scroller must have multiple sources of input/output (db input: local and remote, user input (page controller), graphical outputs: pages and page controller)

Create task in grunt in another file

I wrote simple task in Grunt. Now, I would like to export this task to another file, and I have a problem with it.
How can I find file with my task? This task just searches string from website and putting it in the file. I'm trying load it by: grunt.loadTasks('grunt-find');
I have file (grunt-find) with find.js inside. But it doesn’t work... Can I have to add find.js somewhere else?
Thanks in advance.
grunt.loadTask() will load every JS file in the directory provided as argument; so, basically, you have to do something like:
grunt.loadTasks("tasks");
and you may have a directory called "tasks":
project root
|- tasks
|--- file.js
|- Gruntfile.js
If you find the "directory only" behavior of grunt.loadTask() annoying (i.e. want to keep external tasks definitions next to external task configuration), you can try something like that :
module.exports = function(grunt) {
var env = process.env.NODE_ENV || 'dev';
var _ = require('lodash');
/*** External config & tasks filepaths ***/
//we have 1 base config, and possibly many module-specific config
var configLocations = ['./grunt-config/default_config.js', './grunt-config/**/config.js'];
//we have 1 base tasks definition, and possibly many module-specific config
var tasksLocations = ['./grunt-config/default_tasks.js', './grunt-config/**/tasks.js'];
/* Typical project layout (matching with the globbing pattern above - adapt to your project structure) :
├── Gruntfile.js
├── package.json
├── grunt-config
│ ├── homepage
│ │ └── config.js
│ ├── navigation
│ │ └── config.js
│ ├── module1
│ │ ├── config.js
│ │ └── tasks.js
│ ├── default_config.js
│ ├── default_tasks.js
│ └── template_module_grunt.txt
├── website_directory1
│ ├── mdp
│ ├── multimedia-storage
│ ├── mv-commit.sh
│ ├── query
│ ├── temp
│ └── tmp
└── website_directory2
├── crossdomain.xml
├── css
├── favicon.ico
├── fonts
:
:
:
*/
/***************** External configuration management ***********************************/
var configFiles = grunt.file.expand({
filter: "isFile"
}, configLocations );
grunt.log.writeln('Gathering external configuration files'.underline.green);
grunt.log.writeln("configFiles : " + grunt.log.wordlist(configFiles, {
separator: ', ',
color: 'cyan'
}));
var configArray = configFiles.map(function(file) {
grunt.log.writeln("=> importing : " + file);
return require(file)(grunt, env);
});
var config = {};
configArray.forEach(function(element) {
config = _.merge(config, element);
});
grunt.initConfig(config);
/***************** Task loading & registering *******************************************/
// We load grunt tasks listed in package.json file
require('load-grunt-tasks')(grunt);
/****** External tasks registering ****************/
grunt.log.writeln('Gathering external task files'.underline.green);
var taskFiles = grunt.file.expand({
filter: "isFile"
}, tasksLocations);
grunt.log.writeln("task files : " + grunt.log.wordlist(taskFiles, {
separator: ', ',
color: 'cyan'
}));
taskFiles.forEach(function(path) {
grunt.log.writeln("=> loading & registering : " + path);
require(path)(grunt);
});
grunt.registerTask('default', ['jshint:gruntfile', 'logConfig']);
grunt.registerTask('checkGruntFile', 'Default task - check the gruntfile', function() {
grunt.log.subhead('* Tâche par défaut - aide et vérification du gruntfile *');
grunt.log.writeln('Exécutez "grunt -h" pour avoir plus d\'informations sur les tâches disponibles');
grunt.log.writeln('...');
grunt.log.subhead('Vérification du gruntfile...');
grunt.task.run(['jshint:gruntfile']);
});
//write the generated configuration (for debug)
grunt.registerTask('logConfig', 'Write the generated conf', function() {
//grunt.task.run(['attention:gruntfile']);
grunt.log.subhead('* Configuration générée : *');
grunt.log.writeln(JSON.stringify(config, undefined, 2));
});
};
Source : https://gist.github.com/0gust1/7683132

Resources